SGA‑OISOpen Interoperability Standard
1.0 Draft
SGA-OIS/Reference

Schemas and vocabularies

The normative JSON Schemas, published verbatim, and every registered vocabulary they define.

The JSON Schemas are normative: where prose and schema disagree, the schema wins. Each file is published verbatim, byte for byte the file a bundle carries (04), and every $ref between them is relative, so the directory resolves as published.

Schemas#

filewhat it describes
attest.schema.jsonA device's signature over one contribution (02 section 3a). Carried inline as attest, or afterwards in its own record.attest message when signing must not delay the contribution (02 section 3a.1).
bay-certificate.schema.jsonSGA's signed assertion that a bay met a certification tier. It asserts a TIER and nothing more: what the tier REQUIRES lives in the bundle, so raising a floor does not mean reissuing every certificate in the field.
bay-profile.schema.jsonWhat a bay can actually prove, generated and signed by the engine rather than filled in by an inspector.
capability-descriptor.schema.jsonWhat a participant sends in DESCRIBE. Answers 'what can you do' and 'what do you want in return'. Signed as one document together with identity — signing identity alone would let a device attest to being one thing and then claim capabilities it does not have.
common.schema.jsonShared vocabulary and primitive types. Normative. Units and sign conventions are fixed by spec/00-conventions.md and are not carried in payloads.
course.schema.jsonThe course as a physical thing: its holes, and what each one measures from every set of markers it offers. Sent once at the start of a round, beside the opening scorecard.
denial.schema.jsonThe engine refusing a device admission, in a form the device can act on WITHOUT reading English.
event-rules-package.schema.jsonSigned by SGA and verified by the SGA Engine before it is delivered to a simulator (08 section 1.1), distributed to every participating bay before play. Removes every variable that moves a score and is not the player.
going-away.schema.jsonThe engine closing a connection ON PURPOSE, and saying when to come back. Without this a planned restart is indistinguishable from a crash.
heartbeat.schema.jsonApplication liveness (09 section 4). A device sends its own clock as device_t_ms; only the engine's heartbeat carries t_ms.
notice-response.schema.jsonA player's answer to a notice that carried choices, routed back to whoever asked. Always optional: nothing in a round ever waits on one.
notice-result.schema.jsonThe engine telling a device what became of a notice it raised (12 section 7).
notice.schema.jsonThe engine telling someone something they can act on, or need to know is affecting their record.
player-up.schema.jsonThe record an engine writes when the player at the ball changes (06 section 9.7).
request.schema.jsonA participant asking a bay to change or produce something, rather than asserting a measurement (03 section 1.1).
round-boundary.schema.jsonThe records that say where a round starts and stops (06 section 9.6). A round is the unit everything else is measured against -- a card, a player rating, a tournament result -- and these are the only things in the journal that state its extent.
scorecard.schema.jsonEmitted by the simulator after every player in the round has completed a hole.
session-manifest.schema.jsonThe connection matrix, published by the engine to every participant. The set of manifests for a session is a complete history of the bay's technical configuration for that round.
shot-contribution.schema.jsonWhat a device sends about one shot: the field it is about, and the values it measured in the shot record's own namespaces and shapes.
shot-record.schema.jsonA shot is a transition between two states with a path between them. All distances are ground distances in meters unless the field name says otherwise; all units and sign conventions are fixed by spec/00-conventions.md.
update.schema.jsonThe engine fetching a firmware image on a device's behalf, for hardware that cannot reach the internet itself: a BLE-only accessory with no screen, or a LAN device on a network with no outbound access.

Registered vocabularies#

Every coded value the standard defines, drawn from the schemas. A sender uses one of these or, where a definition allows it, a vendor value under x-<vendor>., and a consumer switches on them.

admissionPath#

common.schema.json#/$defs/admissionPath

operator_enrollment, member_registered, member_introduced, direct

How a device was admitted (13 section 4.1). operator_enrollment: enrolled to the bay by its operator. member_registered: a member's registered device, admitted by their check-in. member_introduced: presented by a member's phone. direct: joined by presenting the bay token itself.

allowanceSource#

common.schema.json#/$defs/allowanceSource

sga, event, operator, local

Where a stroke allowance came from. sga: computed by the association from the member's rating. event: fixed by the Event Rules Package, which overrides everything else for a sanctioned round. operator: a human typed it at the bay. local: the simulator worked it out from figures it stores itself. These are different strengths of evidence and a consumer MUST be able to tell them apart -- a match decided by an operator-entered allowance is not the same claim as one decided by a rated field.

assistEffect#

common.schema.json#/$defs/assistEffect

plays_the_stroke, skips_the_stroke, changes_the_result, changes_the_course, more_than_one_attempt, informs_the_decision

What an assist does to the evidence, which is the only thing a consumer needs to know about it. Six effects, closed: every assist any simulator offers is one of these, whatever its vendor calls it. `plays_the_stroke` -- The software hit a stroke the player did not: auto-putt is the clear case. `skips_the_stroke` -- Strokes that are never played: a gimme distance, a cap on strokes per hole. `changes_the_result` -- Where the ball went is not only what the swing produced: aim assist, shot shaping, automatic compensation for wind or elevation. `changes_the_course` -- The hole is easier than the one described: a larger cup, a removed hazard, an improved lie. `more_than_one_attempt` -- The recorded stroke is not the player's only try, before or after it: mulligans, shot replay, a preview of the putt or the flight. `informs_the_decision` -- Advice only. The player still hits the ball, and the ball goes where the swing sends it: green reading, club recommendation. See 15 section 9.

assistScope#

common.schema.json#/$defs/assistScope

tee_shot, approach, around_green, putting, all

Where on the hole an assist acts, in the ordinary vocabulary of the game: the tee shot on a par 4 or 5, an approach (including a par-3 tee shot), a shot around the green, a putt -- or all of them.

attestationTier#

common.schema.json#/$defs/attestationTier

A0, A1, A2, A3

A0 open: no agreement. A1: the Technology Partner Agreement and a published digest SGA verifies; nothing is signed. A2: also signs what it sends, with a key in firmware or a protected executable. A3: signs with a key in hardware nobody can extract. A result takes the lowest tier in its chain (02 section 2.3). A device MUST NOT claim a tier above what it demonstrates.

bodySegment#

common.schema.json#/$defs/bodySegment

pelvis, thorax, head, lead_shoulder, trail_shoulder, lead_arm, trail_arm, lead_wrist, trail_wrist, lead_knee, trail_knee, lead_foot, trail_foot, hands, club

Lead and trail rather than left and right, so the vocabulary is handedness-independent. A left-handed golfer's lead side is their right.

clubIdMethod#

common.schema.json#/$defs/clubIdMethod

imaged, marker, rfid_nfc, holder_absence, inferred_from_impact, ui_selected, bag_sequence, unknown

HOW the club in a player's hands was identified. A separate axis from field provenance, because the club is a categorical identity claim rather than a measured quantity. imaged: the club head was visually recognized. marker: a sticker, label or dot was read. rfid_nfc: the club announced itself. inferred_from_impact: derived from loft, speed and impact -- a guess with physics behind it. ui_selected: a human told the simulator, which is a claim and not evidence. holder_absence: the club is inferred from what is MISSING from a known set -- a sensed rack with an empty slot, a bag with an RFID reader that can see every club still in it, any holder that knows its own inventory. Strong evidence of what was drawn, though not of what was swung. bag_sequence: assumed from order of play. Everything through holder_absence is evidence; ui_selected and bag_sequence are claims.

denialCode#

common.schema.json#/$defs/denialCode

firmware_below_minimum, firmware_build_denied, model_denied, vendor_denied, unit_denied, serial_required, role_occupied, describe_required, not_your_field, descriptor_too_large, device_id_in_use, bay_full, superseded, associated_elsewhere, ownership_unknown, ownership_disputed

What was refused, as a registered value rather than prose. unit_denied is a bad production batch -- a hardware fault, or units shipped with a key that leaked -- which firmware matching cannot express. serial_required means a batch of this model is denied and this unit will not say which one it is; it may well be a good unit, and the code says so. role_occupied is the only code here that is not about the device itself. Some roles admit exactly one participant -- two simulators driving one bay produce a record composed from two different games -- so the second is refused and told who holds the role, by name. A refusal that does not name the incumbent sends a vendor hunting for a bug that is not theirs. The last three are not refusals of the DEVICE at all, and a vendor should not treat them as faults to fix in firmware. associated_elsewhere means the device belongs to another bay at this facility and carries a redirect saying which; the same connection one engine along would have succeeded. ownership_unknown means no engine here has an association for it and this is not the only engine at the site, so adopting it might capture the device a neighboring bay was waiting for -- a person has to bind it. ownership_disputed means two engines claim it and the claims cannot be ordered; it is deliberately not resolved, because picking one would choose by something irrelevant, like which engine had been busier, and the loser's operator would watch a device leave their bay with nothing to point at.

denialRemedy#

common.schema.json#/$defs/denialRemedy

update_firmware, change_firmware, report_serial, contact_vendor, close_other_software, describe_first, declare_field, reduce_descriptor, change_device_id, retry_later, reconnect, connect_to_bay, wait_for_association

What would fix it. update_firmware moves forward to required_firmware; change_firmware moves OFF the named builds to any other supported one, which may mean backwards; report_serial identifies the unit and retries; contact_vendor is the honest answer where nothing the device can do unattended will help, and saying it stops a device retrying into the void. connect_to_bay and wait_for_association are the two answers to which engine a fixed device belongs to: follow the redirect, or idle and keep advertising until somebody binds the device to a bay. Neither is contact_vendor, which would send an operator to the wrong company.

discoveryPath#

common.schema.json#/$defs/discoveryPath

mdns, unicast_dnssd, ble, dhcp, public_txt, cached, configured, loopback, redirect

How a device found the engine it is talking to (03 section 5). mdns: multicast DNS-SD on the local network, which should normally win. unicast_dnssd: the same records published in the venue's own DNS and browsed by unicast DNS-SD (RFC 6763). cached: a last-known-good address. configured: an address typed by somebody or shipped in a file. cached and configured are NOT evidence that discovery works. loopback: the engine and the device are one machine. redirect: sent here by another engine (03 section 2.0f). RESERVED, not to be reported until specified: dhcp, ble, public_txt.

downgradeReason#

common.schema.json#/$defs/downgradeReason

no_spin_lock, occluded, out_of_range, low_light, no_ball_marker, sensor_fault, calibration_stale, thermal_limit, degraded_link, other

Why a device produced a value of lower quality than it normally can, so the drop is explained rather than merely observed. Recorded because a run of no_spin_lock or low_light is a bay maintenance problem, while calibration_stale is an operator problem, and the two need different answers.

eventSurface#

common.schema.json#/$defs/eventSurface

tee, fairway, first_cut, rough, deep_rough, fescue, sand_bunker, sand_waste, hardpan, pine_straw, divot, cart_path, rock, water, penalty_area, fringe, green, foliage, tree_trunk, netting, wall, fence, post, structure, flagstick, cup, equipment, water_surface, player, unknown

What the ball met at one trajectory event. A SUPERSET of lieSurface, not the same vocabulary: a ball comes to rest on a lie, but in flight it hits things a ball can never lie on -- foliage, netting, a flagstick, a person. Reusing lieSurface here made the standard's own worked example non-conformant, because a ball deflected by a tree struck foliage and foliage is not a lie. Every lieSurface value is included, so a landing, bounce or roll uses exactly the term it would use anywhere else; the additions are only ever things struck. Pair it with `object` for the specific noun: surface is the coded material, object is the free-text thing.

fieldCondition#

common.schema.json#/$defs/fieldCondition

requires_ball_marker, requires_club_marker, requires_clean_ball, indoor_only, outdoor_only, requires_full_flight, requires_min_ceiling, requires_lighting, requires_level_surface, requires_fixed_tee_position, requires_calibration_current, requires_correct_replacement, temperature_range_only, full_swing_only, putting_only, right_handed_only, left_handed_only

What must hold for a field to be valid at its declared provenance. requires_correct_replacement is the one for position-based holder_absence: a slotted rack is only right if clubs go back where they came from, while a tag-reading bag does not care. A REGISTERED vocabulary, not free text, because the spec makes a normative claim about it: a device operating outside its declared conditions is operating outside its certification. Certification cannot check a condition only one vendor understands. Vendor-specific conditions extend under an x- namespace and MUST also declare the nearest core value.

fieldTiming#

common.schema.json#/$defs/fieldTiming

shot_synchronous, standing

shot_synchronous: produced by the act of the shot itself -- ball speed, club path. standing: a state the device asserts and maintains BETWEEN shots -- which club was drawn, what angle the platform reached, whose turn it is. The two need different handling: a standing assertion can be stale, and can be withdrawn.

firmness#

common.schema.json#/$defs/firmness

soft, medium_soft, medium, medium_firm, firm, unknown

Ground firmness where the ball lies. Coarse on purpose: no simulator measures this, and a five-step scale is the most any of them can express honestly. unknown is a legitimate answer and is preferred to a confident guess.

goingAwayReason#

common.schema.json#/$defs/goingAwayReason

shutdown, restart, update, reconfigure, superseded

Why the engine is closing the connection on purpose. The distinction that matters is planned versus crashed. A socket that simply closes is indistinguishable from an engine that died, so a participant applies its normal backoff -- capped at five seconds, which is right for a crash and wrong for a thirty-second update. superseded means the participant has lost a role to another, and is the one value that normally carries no reconnect interval: there is nothing to come back to until something changes at the bay.

handedness#

common.schema.json#/$defs/handedness

right, left, unknown

Which side the player swings from. DESCRIPTIVE ONLY -- no implementation may flip a sign based on it (00 section 2). It is carried as TWO fields, never one: player.sim_handedness is what the software is configured for and player.lm_handedness is what a sensor in the room observed -- a launch monitor, or a camera. A simulator set for a right-hander while a left-hander plays mirrors every number after it, and that is only visible if both answers survive (15 section 8).

integrityStatus#

common.schema.json#/$defs/integrityStatus

verified, verified_with_exception, pending_review, unverified

unverified is a status, not a rejection: the round is still recorded and still visible to the player; it does not feed sanctioned ranking.

lieSurface#

common.schema.json#/$defs/lieSurface

tee, fairway, first_cut, rough, deep_rough, fescue, sand_bunker, sand_waste, hardpan, pine_straw, divot, cart_path, rock, water, penalty_area, fringe, green, unknown

Vendor-specific surfaces extend under an x- namespace and MUST map to the nearest core value.

noticeAudience#

common.schema.json#/$defs/noticeAudience

round, slot, operator

round: everyone playing. slot: one player, addressed BY SLOT and never by name -- the app resolves it and the bay stays ignorant of who anyone is. operator: staff, for something a player cannot fix.

noticeCode#

common.schema.json#/$defs/noticeCode

club_ambiguous, source_degraded, device_left, unmeasured_stroke, settings_divergent, settings_unattested, calibration_expired, unbound_wearable, time_unverified, media_capture_suppressed, round_ending, bay_offline, update_available, device_denied, certification_shortfall, calibration_expiring, certification_expiring, bay_ineligible_for_event, device_message, device_question

A registered vocabulary because consumers act on it. device_message and device_question are the two a PARTICIPANT may raise rather than the engine; everything else is engine-only. an app renders and localizes from the code, and the English `text` is a fallback for a surface with no app behind it.

noticeUrgency#

common.schema.json#/$defs/noticeUrgency

interrupt, attention, background, record

How much of the player's attention this deserves -- NOT which channel to use. The engine has no idea whether someone wants a push, an SMS or nothing until the round summary; the platform maps urgency to channel using the member's own preferences. interrupt: they are over the ball and can fix it now. attention: they should see it before the next hole. background: the round summary is soon enough. record: journal only, no human involved.

penaltyReason#

common.schema.json#/$defs/penaltyReason

water, out_of_bounds, lost_ball, unplayable, wrong_ball, slow_play, equipment, provisional_taken, conceded, other

Why a penalty stroke was added, coded so scoring and player rating can treat the categories differently -- a lost ball and a slow-play penalty are not the same evidence about how someone played.

provenance#

common.schema.json#/$defs/provenance

measured, calculated, inferred, absent

measured: a sensor on this device directly observed the quantity on this shot. calculated: deterministically computed from quantities measured on this same shot; MUST declare `from`. inferred: estimated using assumptions or history not measured on this shot; MUST declare `model`.

retractionReason#

common.schema.json#/$defs/retractionReason

ambiguous, out_of_range, sensor_fault, superseded, reset, obstructed, powered_down, unknown

Why a device is withdrawing a standing assertion. 'ambiguous' is the common one: the device can tell that its previous answer is no longer safe, but cannot tell what the new answer is.

role#

common.schema.json#/$defs/role

sensor.ball, sensor.club, sensor.club_id, sensor.equipment, sensor.swing, sensor.pressure, sensor.putting, sensor.environment, sensor.player, simulator, actuator, display, recorder, officiator, courier

One part a participant plays in a bay. Additive: a device may hold several, and roles determine defaults rather than permissions -- permissions come from the connection matrix for what a participant may ASSERT, and from the request table (03 section 1.1) for what it may ASK. There is deliberately no `operator` role: operator authority arrives only by relay from the cloud, which authorized it against the bay's claim, so nothing connecting locally can claim it.

roundDisposition#

common.schema.json#/$defs/roundDisposition

complete, partial_recorded, abandoned, disqualified

partial_recorded is the normal outcome for an unfinished round and is not a failure state. Partial rounds are first-class records; whether one posts is a query, not an ingest decision.

scoringFormat#

common.schema.json#/$defs/scoringFormat

stroke, stableford, match, scramble, best_ball, alternate_shot, skins

How the card is scored. Recorded rather than inferred: the same strokes mean different things under stableford, match play and skins, and a consumer that guessed would be wrong for every format but the one it assumed.

sensorType#

common.schema.json#/$defs/sensorType

radar_doppler, camera_photometric, camera_infrared, camera_highspeed, camera_stereo, lidar, time_of_flight, optical_grid, photometer, pressure_mat, strain_gauge, imu, magnetometer, hall_effect, encoder, rfid_nfc, ultrasonic, acoustic, gnss, environmental_probe, anemometer, simulated, none

One sensing technology. Real devices routinely combine several -- dual radar with a camera, quadrascopic photometric cameras, overhead high-speed infrared -- so the device-level declaration is an ARRAY and multiple entries are the normal case, not an exception. A provided field may additionally name the specific sensors behind it. Declarative only: it does not affect routing. Collected because sensing modality is the strongest predictor of which fields a device can measure rather than infer, and because the aggregate is genuinely useful back to manufacturers. Some pairs here are easy to conflate, and the distinction is the whole reason both exist: - magnetometer measures the AMBIENT field, which is how a device works out heading or orientation. hall_effect detects a magnet passing or sitting close to it -- club head over a mat, a magnet in a shaft, a rotating shaft counted per turn. Same physics, different question. - encoder is position feedback on something that moves under control: a tilting platform, a robotic tee. A Hall sensor is sometimes wired to do this job, but an encoder is the part that exists for it. - lidar scans. time_of_flight is a single-point range finder, which is a different cost, a different footprint and a far narrower set of fields. - photometer reports illuminance. It is worth declaring because it is what says whether a camera-based system can work at all in this bay. - environmental_probe covers air temperature, humidity and pressure together, because one component routinely does all three. anemometer is separate: wind is its own instrument, and outdoors it is the only ambient value that materially moves a ball. rfid_nfc reads a tag. The standard already ranks it above every other way of identifying a club (see club.identification.method), so the reader that does it must be able to say what it is.

shotKind#

common.schema.json#/$defs/shotKind

stroke, whiff, provisional, mulligan, practice, gimme, concession, drop, penalty

What kind of stroke this record represents. Kinds other than stroke/whiff/provisional/mulligan/practice involve no swing and carry no ball, club or trajectory data -- but they still produce a record, because every stroke added to a score must be reportable and auditable.

specSource#

common.schema.json#/$defs/specSource

member_bag, sim_bag_setup, rfid_tag, rack_slot, fitting_record, operator_entered, manufacturer_lookup, unknown

Where an equipment specification came from. member_bag resolves in the cloud on claim; the others are known at the bay. It matters for the same reason club identification does: an equipment corpus is only worth money if you can filter to rows where the equipment was actually known rather than assumed.

strokeActor#

common.schema.json#/$defs/strokeActor

player, simulator_auto, operator, official

Who added this stroke. 'operator' and 'official' are high-signal integrity events: a human adjusting a score is exactly what an audit trail exists to capture.

swingEvent#

common.schema.json#/$defs/swingEvent

address, takeaway, top, transition, impact, finish

The named moments every biomechanical measurement anchors to. Without a shared set of these, two systems reporting 'pelvis turn at the top' are not reporting the same thing.

trajectoryPhase#

common.schema.json#/$defs/trajectoryPhase

launch, flight, apex, interaction, landing, bounce, roll, rest, penalty

Which part of the ball's life a trajectory event belongs to, from launch through flight, whatever it interacts with, landing, bounce and roll, to rest -- or penalty, where the ball's own physics stopped mattering. apex marks the top of the flight: a single event, carried so the vertical profile is readable without re-deriving it from the point series, and so a shot that never got airborne is distinguishable from one whose apex nobody recorded.

wantImportance#

common.schema.json#/$defs/wantImportance

opportunistic, requested, required

How much a participant needs a field, and -- because it is normally derived from HOW the field was asked for rather than stated -- also a declaration of what the product actually implements. requested is the default for a field named explicitly: the participant is saying it consumes this field, which is the only way anyone can tell an implemented feature from an unimplemented one. opportunistic is the default for a field matched by a wildcard: take it if it is there, and do not report it as unmet if it is not. Without that last part a wildcard in a sparse bay buries the real demand signal under fields nobody was actually waiting for. required means the participant cannot perform its declared role without the field, and an unmet one is surfaced to the operator. It MUST name a field: 'I cannot operate without some unspecified set of fields' is not something anyone can act on. Reserve it for what it says -- a descriptor marking many wants required is advertising fragility, not importance.

wantReason#

common.schema.json#/$defs/wantReason

calibration, player_setup, compensation, validation, actuation, display, recording, scoring, power_management, safety, diagnostics, other

Why a participant wants a field, as a countable code. OPTIONAL and never a gate: it exists only so the aggregate -- what does the ecosystem actually need -- can be counted rather than text-mined. Pair it with free-text `reason` for anything the list does not cover, and send `other` plus text rather than forcing a fit.

Simulated Golf Association · SGA-OIS · generated from the specification sources