{
  "contract": "vg.control.v2.core",
  "revision": "2.0-draft.1",
  "status": "shared-vectors-not-client-certification",
  "cursor_vectors": [
    {"id": "next-with-aggregate-gap", "last_seq": "18", "incoming_seq": "19", "last_aggregate_version": "81", "incoming_aggregate_version": "84", "same_binding": true, "same_stream_epoch": true, "expect": "apply"},
    {"id": "duplicate", "last_seq": "19", "incoming_seq": "19", "same_binding": true, "same_stream_epoch": true, "expect": "ignore"},
    {"id": "late", "last_seq": "19", "incoming_seq": "17", "same_binding": true, "same_stream_epoch": true, "expect": "ignore"},
    {"id": "gap", "last_seq": "19", "incoming_seq": "21", "same_binding": true, "same_stream_epoch": true, "expect": "resync"},
    {"id": "old-binding", "last_seq": "19", "incoming_seq": "20", "same_binding": false, "same_stream_epoch": true, "expect": "ignore"},
    {"id": "new-epoch-needs-snapshot", "last_seq": "19", "incoming_seq": "20", "same_binding": true, "same_stream_epoch": false, "expect": "resync"},
    {"id": "beyond-js-safe-integer", "last_seq": "9007199254740992", "incoming_seq": "9007199254740993", "same_binding": true, "same_stream_epoch": true, "expect": "apply"},
    {"id": "uint64-tail", "last_seq": "18446744073709551614", "incoming_seq": "18446744073709551615", "same_binding": true, "same_stream_epoch": true, "expect": "apply"}
  ],
  "scenarios": [
    {"id": "V2-01", "invariants": ["INV-01", "INV-02"], "given": "Legacy server ignores protocol offer; client requires v2.", "when": ["Receive result=1 without control_protocol."], "then": ["Do not send v2 commands or auto-join.", "Report unsupported protocol without deleting account."]},
    {"id": "V2-02", "invariants": ["INV-05", "INV-06", "INV-07"], "given": "Two commands of the same name have distinct IDs A and B.", "when": ["Receive B result before A.", "Duplicate B result."], "then": ["Resolve B only by B ID, A remains pending.", "Duplicate result causes no second effect."]},
    {"id": "V2-03", "invariants": ["INV-05", "INV-06"], "given": "A committed, but socket died before its response.", "when": ["Resume to generation 8.", "Retry A with the same intent and new binding."], "then": ["One domain mutation, same saved result.", "Do not allocate another publication or recording."]},
    {"id": "V2-04", "invariants": ["INV-05"], "given": "A used with enabled=false.", "when": ["Retry A with enabled=true."], "then": ["idempotency_conflict; zero new mutations."]},
    {"id": "V2-05", "invariants": ["INV-08", "INV-09"], "given": "Logical connection restored on gateway B, generation 8.", "when": ["Gateway A emits delayed close and device delete for generation 7."], "then": ["No participant removal and no cursor changes.", "Healthy RTC is not restarted."]},
    {"id": "V2-06", "invariants": ["INV-03", "INV-13"], "given": "Same user on phone and laptop, different participant/publication IDs.", "when": ["Phone stops its microphone."], "then": ["Laptop microphone remains published.", "Other-device media is not filtered merely because user_id equals self."]},
    {"id": "V2-07", "invariants": ["INV-10", "INV-11"], "given": "Snapshot has two parts at seq=40, client has only part 0.", "when": ["Receive ready barrier at 40, then live event 41.", "Receive part 1."], "then": ["Before part 1: no partial state or Ready UI.", "Apply full snapshot atomically, then event 41."]},
    {"id": "V2-08", "invariants": ["INV-10", "INV-11"], "given": "ACL changed and stream epoch 2 replaces epoch 1.", "when": ["Receive snapshot for epoch 2 and old event from epoch 1."], "then": ["Old authorized data is purged according to new projection.", "Old replay cannot leak newly forbidden content."]},
    {"id": "V2-09", "invariants": ["INV-12"], "given": "Publication persists, RTC endpoint or negotiation changed.", "when": ["Old answer/ICE arrives for prior route/endpoint/negotiation."], "then": ["Discard before touching current peer.", "No duplicate device_params/create."]},
    {"id": "V2-10", "invariants": ["INV-14"], "given": "Auth database unavailable or refresh outcome unknown.", "when": ["Receive temporarily_unavailable or timeout."], "then": ["Retain account, refresh fallback and chat history.", "No auto-registration or password reset."]},
    {"id": "V2-11", "invariants": ["INV-15"], "given": "User explicitly left; participation tombstone committed.", "when": ["A stale resume or delayed media offer arrives."], "then": ["Do not resurrect call membership or capture."]},
    {"id": "V2-12", "invariants": ["INV-16"], "given": "VAD signal received for live source with 3000ms TTL.", "when": ["No subsequent signal; advance monotonic clock past TTL."], "then": ["Reset speaking indicator.", "No unread increment and no durable seq advancement."]},
    {"id": "V2-13", "invariants": ["INV-04", "INV-09"], "given": "Two tenants or two calls have equal local IDs/tags.", "when": ["Frame from other tenant/call arrives."], "then": ["Reject wrong scope before model/media changes."]},
    {"id": "V2-14", "invariants": ["INV-06"], "given": "Request coroutine/Promise timed out after send.", "when": ["Committed result arrives late; client reconnects."], "then": ["Timeout not treated as rollback.", "Status reconciliation uses original command ID."]},
    {"id": "V2-15", "invariants": ["INV-10"], "given": "Browser retained cursor but local state was evicted.", "when": ["Cold start on another gateway."], "then": ["Request snapshot, not replay onto empty state."]},
    {"id": "V2-16", "invariants": ["INV-01"], "given": "Raw JSON contains duplicate object keys.", "when": ["Parse before conversion to ordinary map/object."], "then": ["Reject raw frame, not last-wins interpretation."]},
    {"id": "V2-17", "invariants": ["INV-05", "INV-06"], "given": "Command retry window expired, result no longer available.", "when": ["Old command is retransmitted."], "then": ["Return unknown/expired outcome, never treat it as a new mutation."]},
    {"id": "V2-18", "invariants": ["INV-03", "INV-08"], "given": "Two cold browser tabs or KMP processes use same account.", "when": ["Both refresh and reconnect while one response is lost."], "then": ["No account loss, accidental extra guest or capture takeover.", "Active instances have distinct instance/resume identities."]}
  ]
}
