feat(platform): complete the Gelios external data loop
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"updatedAt": "2026-07-16",
|
||||
"version": "1.1.0",
|
||||
"updatedAt": "2026-07-20",
|
||||
"aliases": [
|
||||
{ "alias": "gelios", "canonicalId": "gelios.integration" },
|
||||
{ "alias": "gelius", "canonicalId": "gelios.integration" },
|
||||
@@ -13,7 +13,12 @@
|
||||
{ "alias": "последняя телеметрия гелиос", "canonicalId": "gelios.telemetry_snapshot" },
|
||||
{ "alias": "сырая телеметрия гелиос", "canonicalId": "gelios.raw_telemetry_message" },
|
||||
{ "alias": "позиция объекта гелиос", "canonicalId": "gelios.position_fix" },
|
||||
{ "alias": "статус объекта гелиос", "canonicalId": "gelios.operational_status" },
|
||||
{ "alias": "статус связи гелиос", "canonicalId": "gelios.signal_state" },
|
||||
{ "alias": "на связи гелиос", "canonicalId": "gelios.signal_state" },
|
||||
{ "alias": "не на связи гелиос", "canonicalId": "gelios.signal_state" },
|
||||
{ "alias": "статус движения гелиос", "canonicalId": "gelios.movement_state" },
|
||||
{ "alias": "в движении гелиос", "canonicalId": "gelios.movement_state" },
|
||||
{ "alias": "неподвижные гелиос", "canonicalId": "gelios.movement_state" },
|
||||
{ "alias": "датчик гелиос", "canonicalId": "gelios.sensor_definition" },
|
||||
{ "alias": "показание датчика", "canonicalId": "gelios.sensor_reading" },
|
||||
{ "alias": "калибровка датчика", "canonicalId": "gelios.sensor_conversion" },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"updatedAt": "2026-07-16",
|
||||
"version": "1.1.0",
|
||||
"updatedAt": "2026-07-20",
|
||||
"entities": [
|
||||
{ "id": "gelios.integration", "name": "Gelios Integration", "surface": "platform", "status": ["product-required", "source-evidenced"], "authority": "NDC L2 connection", "summary": "One tenant-scoped Gelios provider connection backed by one opaque NDC L2 credential reference. It owns no UI, renderer, secret value or provider-specific Platform service." },
|
||||
{ "id": "gelios.access_scope", "name": "Gelios Access Scope", "surface": "platform", "status": ["product-required", "source-evidenced"], "authority": "Gelios credential visibility + NDC L2 connection policy", "summary": "Dynamic collection boundary: selected safe-read capabilities include every entity visible to the bound credential on each run. Entity allowlists and provider-group business filters are not part of collection scope." },
|
||||
@@ -11,7 +11,9 @@
|
||||
{ "id": "gelios.raw_telemetry_message", "name": "Raw Telemetry Message", "surface": "telemetry", "status": ["source-evidenced"], "authority": "Gelios Pro", "summary": "Provider hardware message preserved only behind restricted raw-data policy; it is not the default Studio or analytics contract." },
|
||||
{ "id": "gelios.position_fix", "name": "Position Fix", "surface": "telemetry", "status": ["source-evidenced", "product-required"], "authority": "NDC L2 semantic mapping", "summary": "Time-qualified geographic position with latitude, longitude, height, course, speed, satellite and quality attributes for one unit." },
|
||||
{ "id": "gelios.telemetry_parameter", "name": "Telemetry Parameter", "surface": "telemetry", "status": ["source-evidenced"], "authority": "Gelios Pro", "summary": "Name/value parameter received with a message. Parameters are dynamic and require a namespaced registry and whitelist before product exposure." },
|
||||
{ "id": "gelios.operational_status", "name": "Operational Status", "surface": "telemetry", "status": ["source-evidenced", "product-required"], "authority": "NDC L2 semantic mapping", "summary": "Derived unit state such as active, stale, moving, parked, no-position or low-GPS; distinct from raw provider message fields." },
|
||||
{ "id": "gelios.signal_state", "name": "Gelios Signal State", "surface": "telemetry", "status": ["source-evidenced", "product-required"], "authority": "Gelios monitoring client + monitoring-config", "summary": "Official Gelios unit connection filter. Field signal_state has exactly active (На связи / Active units) and inactive (Не на связи / Inactive units). The client resolves its active window from signalActiveDuration and signalSomewhatInactiveDuration; a missing message is inactive. A connection profile may provide one explicit positive fallback when both account values are empty, otherwise publication fails closed.", "valueContract": { "field": "signal_state", "values": [{ "value": "active", "label": "На связи", "sourceLabel": "Active units" }, { "value": "inactive", "label": "Не на связи", "sourceLabel": "Inactive units" }], "sourcePaths": ["lastMsg.time", "monitoringConfig.signalActiveDuration", "monitoringConfig.signalSomewhatInactiveDuration"], "rules": ["missing_last_message.inactive", "last_message_age_lt_resolved_monitoring_limit.active", "otherwise.inactive"] } },
|
||||
{ "id": "gelios.movement_state", "name": "Gelios Movement State", "surface": "telemetry", "status": ["source-evidenced", "product-required"], "authority": "Gelios monitoring client", "summary": "Official Gelios unit movement filter. Field movement_state has exactly moving (В движении / Moving units) and stopped (Неподвижные / Stopped units). The monitoring client classifies integer last-message speed greater than 2 as moving; a missing message or speed not greater than 2 is stopped.", "valueContract": { "field": "movement_state", "values": [{ "value": "moving", "label": "В движении", "sourceLabel": "Moving units" }, { "value": "stopped", "label": "Неподвижные", "sourceLabel": "Stopped units" }], "sourcePaths": ["lastMsg.speed"], "rules": ["integer_speed_gt_2.moving", "otherwise.stopped"] } },
|
||||
{ "id": "gelios.operational_status", "name": "Legacy NDC Operational Status", "surface": "telemetry", "status": ["tech-debt-noncanonical"], "authority": "Legacy NDC mapping only", "summary": "Noncanonical legacy aggregate retained only to identify and retire old v1/v2 contracts. It is not a Gelios status, must not enter new Data Products and must not drive Foundry filters, counters, colours or classes." },
|
||||
{ "id": "gelios.sensor_definition", "name": "Sensor Definition", "surface": "domain", "status": ["source-evidenced"], "authority": "Gelios Pro", "summary": "Declared sensor on a unit with message parameter, type, unit of measure, visibility and optional fuel semantics." },
|
||||
{ "id": "gelios.sensor_reading", "name": "Sensor Reading", "surface": "telemetry", "status": ["source-evidenced", "product-required"], "authority": "NDC L2 semantic mapping", "summary": "Time-qualified normalized reading of a defined sensor, retaining both numeric value and human-readable representation only when permitted by a versioned field policy." },
|
||||
{ "id": "gelios.sensor_conversion", "name": "Sensor Conversion", "surface": "domain", "status": ["source-evidenced"], "authority": "Gelios Pro", "summary": "Calibration/conversion rule or row for translating raw sensor values into operational measures." },
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"updatedAt": "2026-07-16",
|
||||
"version": "1.1.0",
|
||||
"updatedAt": "2026-07-20",
|
||||
"sourceRoots": [
|
||||
{
|
||||
"surface": "gelios-rest",
|
||||
"path": "https://api.geliospro.com/docs",
|
||||
"mode": "OpenAPI inspection and safe read-only runtime audit; no write routes executed"
|
||||
"path": "https://api.geliospro.com/openapi.json",
|
||||
"mode": "Official OpenAPI inspection for GET /api/v1/units and GET /api/v1/users/me/monitoring-config; no write routes executed"
|
||||
},
|
||||
{
|
||||
"surface": "gelios-monitoring-client",
|
||||
"path": "https://geliospro.com/js/app.js",
|
||||
"mode": "Official public monitoring-client inspection for active/inactive and moving/stopped derivation rules"
|
||||
},
|
||||
{
|
||||
"surface": "gelios-monitoring-localization",
|
||||
"path": "https://geliospro.com/tmp/ru-RU.js",
|
||||
"mode": "Official public Russian labels for Active units, Inactive units, Moving units and Stopped units"
|
||||
},
|
||||
{
|
||||
"surface": "ndc-l2-legacy-evidence",
|
||||
@@ -28,6 +38,8 @@
|
||||
"gelios.unit",
|
||||
"gelios.telemetry_snapshot",
|
||||
"gelios.position_fix",
|
||||
"gelios.signal_state",
|
||||
"gelios.movement_state",
|
||||
"gelios.sensor_definition",
|
||||
"gelios.sensor_reading",
|
||||
"gelios.geozone",
|
||||
@@ -42,6 +54,7 @@
|
||||
"restrictions": [
|
||||
"Do not copy access/refresh tokens, passwords, hardware decrypt keys or runtime snapshots into Ontology Core.",
|
||||
"Do not make a current NDC L2 workflow node, renderer entities or provider group names canonical domain identities.",
|
||||
"Do not promote operational_status, freshness, GPS quality, missing position or unknown fallback buckets to Gelios monitoring states. The official filter value contracts are signal_state active/inactive and movement_state moving/stopped only.",
|
||||
"Do not invoke command send, create, update, delete or purge routes as ontology evidence.",
|
||||
"Robot2B pilot counts (107 credential-visible units and 95 legacy snapshot units) are historical evidence only; canonical collection scope is dynamically all entities visible to the bound credential.",
|
||||
"Do not bulk-download history, media or full geozone geometry before collection policy and storage architecture are approved."
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"updatedAt": "2026-07-16",
|
||||
"version": "1.1.0",
|
||||
"updatedAt": "2026-07-20",
|
||||
"rules": [
|
||||
{
|
||||
"id": "guardrail.gelios.ontology_not_runtime_store",
|
||||
@@ -32,6 +32,12 @@
|
||||
"summary": "Cesium pins and labels must consume stable gelios.unit and normalized gelios.position_fix through map.moving_object. They must not use raw message IDs, transient Cesium entity IDs or provider credentials as map identity.",
|
||||
"entityIds": ["gelios.unit", "gelios.position_fix", "gelios.telemetry_snapshot", "map.moving_object", "map.pin", "map.label"]
|
||||
},
|
||||
{
|
||||
"id": "guardrail.gelios.monitoring_states_are_closed",
|
||||
"severity": "error",
|
||||
"summary": "Gelios monitoring state is a closed source-evidenced contract: signal_state is active or inactive; movement_state is moving or stopped. NDC L2, Data Products and Foundry must not add unknown, stale, freshness, GPS, position-quality, parked, no-position or aggregate operational-status values.",
|
||||
"entityIds": ["gelios.telemetry_snapshot", "gelios.signal_state", "gelios.movement_state", "gelios.operational_status", "map.state_facet", "map.style_profile"]
|
||||
},
|
||||
{
|
||||
"id": "guardrail.gelios.raw_and_pii_are_restricted",
|
||||
"severity": "warning",
|
||||
@@ -56,6 +62,9 @@
|
||||
["gelios.unit", "gelios.tracker_device"],
|
||||
["gelios.telemetry_snapshot", "gelios.raw_telemetry_message"],
|
||||
["gelios.position_fix", "map.moving_object"],
|
||||
["gelios.signal_state", "gelios.movement_state"],
|
||||
["gelios.signal_state", "gelios.operational_status"],
|
||||
["gelios.movement_state", "gelios.operational_status"],
|
||||
["gelios.command_template", "gelios.command_dispatch"],
|
||||
["gelios.command_dispatch", "gelios.command_delivery"],
|
||||
["gelios.geozone", "map.zone"]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "gelios",
|
||||
"version": "1.0.0",
|
||||
"updatedAt": "2026-07-16",
|
||||
"version": "1.1.0",
|
||||
"updatedAt": "2026-07-20",
|
||||
"status": "product-required/source-evidenced",
|
||||
"summary": "Provider-neutral Gelios Pro telemetry, fleet, spatial, reporting and command-domain ontology. It describes canonical meanings and contracts; it does not store credentials, account scope or runtime telemetry."
|
||||
"summary": "Source-evidenced Gelios Pro telemetry, fleet, spatial, reporting and command-domain ontology. Unit monitoring exposes only the official signal and movement value contracts; consumers may not invent additional operational states."
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"updatedAt": "2026-07-16",
|
||||
"version": "1.1.0",
|
||||
"updatedAt": "2026-07-20",
|
||||
"relations": [
|
||||
{ "id": "gelios.integration.is_provider_connection", "from": ["gelios.integration"], "to": ["integration.connection"], "status": "product-required", "summary": "A Gelios integration is a tenant-scoped instance of the common external provider connection contract." },
|
||||
{ "id": "gelios.access_scope.specializes_integration_scope", "from": ["gelios.access_scope"], "to": ["integration.access_scope"], "status": "product-required", "summary": "Gelios credential-visible capability scope specializes the common provider access-scope contract." },
|
||||
@@ -17,7 +17,10 @@
|
||||
{ "id": "gelios.unit.has_telemetry_snapshot", "from": ["gelios.unit"], "to": ["gelios.telemetry_snapshot"], "status": "product-required", "summary": "The versioned NDC L2 mapping produces normalized current telemetry for every valid returned unit." },
|
||||
{ "id": "gelios.raw_message.normalizes_to_snapshot", "from": ["gelios.raw_telemetry_message"], "to": ["gelios.telemetry_snapshot"], "status": "product-required", "summary": "Restricted raw provider telemetry can be normalized into the stable snapshot contract." },
|
||||
{ "id": "gelios.telemetry_snapshot.has_position_fix", "from": ["gelios.telemetry_snapshot"], "to": ["gelios.position_fix"], "status": "product-required", "summary": "A current telemetry snapshot can carry one time-qualified position fix." },
|
||||
{ "id": "gelios.telemetry_snapshot.has_operational_status", "from": ["gelios.telemetry_snapshot"], "to": ["gelios.operational_status"], "status": "product-required", "summary": "The semantic mapping derives normalized operational state from telemetry and data-quality policy." },
|
||||
{ "id": "gelios.telemetry_snapshot.has_signal_state", "from": ["gelios.telemetry_snapshot"], "to": ["gelios.signal_state"], "status": "product-required", "summary": "The current snapshot carries the exact Gelios active/inactive monitoring classification derived from official message-age settings." },
|
||||
{ "id": "gelios.telemetry_snapshot.has_movement_state", "from": ["gelios.telemetry_snapshot"], "to": ["gelios.movement_state"], "status": "product-required", "summary": "The current snapshot carries the exact Gelios moving/stopped monitoring classification derived from official last-message speed logic." },
|
||||
{ "id": "gelios.signal_state.is_map_state_facet", "from": ["gelios.signal_state"], "to": ["map.state_facet"], "status": "product-required", "summary": "Foundry may expose the signal_state value contract as a generic map facet without changing its values or labels." },
|
||||
{ "id": "gelios.movement_state.is_map_state_facet", "from": ["gelios.movement_state"], "to": ["map.state_facet"], "status": "product-required", "summary": "Foundry may expose the movement_state value contract as a generic map facet without changing its values or labels." },
|
||||
{ "id": "gelios.telemetry_snapshot.has_parameter", "from": ["gelios.telemetry_snapshot"], "to": ["gelios.telemetry_parameter"], "status": "source-evidenced", "summary": "A provider snapshot can contain dynamic telemetry parameters." },
|
||||
{ "id": "gelios.unit.has_sensor", "from": ["gelios.unit"], "to": ["gelios.sensor_definition"], "status": "source-evidenced", "summary": "A unit exposes zero or more sensor definitions." },
|
||||
{ "id": "gelios.sensor_definition.has_conversion", "from": ["gelios.sensor_definition"], "to": ["gelios.sensor_conversion"], "status": "source-evidenced", "summary": "A sensor can define conversion/calibration rows." },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"updatedAt": "2026-07-12",
|
||||
"version": "0.2.0",
|
||||
"updatedAt": "2026-07-19",
|
||||
"entities": [
|
||||
{ "id": "map.view", "name": "Map View", "surface": "map", "status": ["product-required"], "authority": "NDC Module Studio + Ontology Core", "summary": "Provider-neutral map work view composed by an approved Map Page template." },
|
||||
{ "id": "map.viewport", "name": "Map Viewport", "surface": "map", "status": ["source-evidenced"], "authority": "NDC Module Studio", "summary": "Camera/view state including position, orientation, zoom or height, focus and saved view presets." },
|
||||
@@ -9,6 +9,7 @@
|
||||
{ "id": "map.grid_layer", "name": "Map Grid Layer", "surface": "map", "status": ["source-evidenced"], "authority": "NDC Module Studio", "summary": "Planetary or situational grid with configurable modes, levels of detail, step, radius and visual primitives." },
|
||||
{ "id": "map.place_target", "name": "Map Place Target", "surface": "map", "status": ["source-evidenced"], "authority": "NDC Module Studio", "summary": "Geographic place target such as a city or territory with location, pulse, radius and distance-dependent presentation." },
|
||||
{ "id": "map.moving_object", "name": "Map Moving Object", "surface": "map", "status": ["source-evidenced"], "authority": "NDC domain source", "summary": "Dynamic spatial object such as transport, robot or vehicle with identity, position, movement and status." },
|
||||
{ "id": "map.state_facet", "name": "Map State Facet", "surface": "map", "status": ["product-required"], "authority": "Bound Data Product + Ontology Core", "summary": "A state dimension explicitly declared by the bound Data Product and its ontology value contract. Map and Foundry have no default facets and may not add values, fallback buckets or classifications of their own." },
|
||||
{ "id": "map.pin", "name": "Map Pin", "surface": "map", "status": ["source-evidenced"], "authority": "NDC Module Studio", "summary": "Reusable provider-neutral elevated-spike presentation with ground anchor, stem, head, label anchor, semantic colour/status and camera-height visibility rules." },
|
||||
{ "id": "map.label", "name": "Map Label", "surface": "map", "status": ["source-evidenced", "product-required"], "authority": "NDC Module Studio", "summary": "Reusable information plate attached to a spatial subject with style, size variant, anchor, offset and visibility rules." },
|
||||
{ "id": "map.zone", "name": "Map Zone", "surface": "map", "status": ["source-evidenced"], "authority": "NDC domain source", "summary": "Polygon or multipolygon zone, sector or geofence with optional height, extrusion and level-dependent style." },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"updatedAt": "2026-07-12",
|
||||
"version": "0.2.0",
|
||||
"updatedAt": "2026-07-19",
|
||||
"rules": [
|
||||
{
|
||||
"id": "guardrail.map.provider_neutral_domain",
|
||||
@@ -26,6 +26,12 @@
|
||||
"summary": "Transport, station, city and other labels must use the shared Map Label contract with semantic variants instead of source-specific label implementations.",
|
||||
"entityIds": ["map.label", "map.moving_object", "map.station", "map.stop", "map.terminal", "map.place_target"]
|
||||
},
|
||||
{
|
||||
"id": "guardrail.map.presentation_uses_normalized_state_facets",
|
||||
"severity": "error",
|
||||
"summary": "Foundry presentation classes, filters, counters and sorting must consume only state facets and exact values declared by the bound Data Product and Ontology value contract. Renderer and profile code must not classify raw values or add fallback states.",
|
||||
"entityIds": ["map.moving_object", "map.state_facet", "map.style_profile", "map.renderer_adapter"]
|
||||
},
|
||||
{
|
||||
"id": "guardrail.map.mass_geometry_requires_strategy",
|
||||
"severity": "warning",
|
||||
@@ -42,6 +48,7 @@
|
||||
"blockedConflations": [
|
||||
["map.view", "map.renderer_adapter"],
|
||||
["map.moving_object", "map.pin"],
|
||||
["map.state_facet", "map.style_profile"],
|
||||
["map.station", "map.label"],
|
||||
["map.route", "map.track_segment"],
|
||||
["map.visibility_rule", "map.provider_capability"],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "map",
|
||||
"version": "0.1.0",
|
||||
"updatedAt": "2026-07-12",
|
||||
"version": "0.2.0",
|
||||
"updatedAt": "2026-07-19",
|
||||
"status": "product-required/source-evidenced",
|
||||
"summary": "Provider-neutral map domain ontology for NDC Module Studio views, spatial entities, transport layers, visibility rules, selection, and replaceable renderer adapters."
|
||||
"summary": "Provider-neutral map domain ontology for NDC Module Studio views, spatial entities, orthogonal state facets, transport layers, visibility rules, selection, and replaceable renderer adapters."
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"updatedAt": "2026-07-12",
|
||||
"version": "0.2.0",
|
||||
"updatedAt": "2026-07-19",
|
||||
"relations": [
|
||||
{ "id": "map.view.uses_viewport", "from": ["map.view"], "to": ["map.viewport"], "status": "product-required", "summary": "A Map View owns provider-neutral viewport state." },
|
||||
{ "id": "map.view.contains_layer", "from": ["map.view"], "to": ["map.base_layer", "map.building_layer", "map.grid_layer"], "status": "product-required", "summary": "A Map View composes enabled spatial layers." },
|
||||
{ "id": "map.view.displays_subject", "from": ["map.view"], "to": ["map.place_target", "map.moving_object", "map.zone", "map.route", "map.track_segment", "map.station", "map.stop", "map.terminal"], "status": "product-required", "summary": "A Map View displays domain subjects through approved page slots." },
|
||||
{ "id": "map.moving_object.uses_pin", "from": ["map.moving_object"], "to": ["map.pin"], "status": "product-required", "summary": "A moving object can use the shared pin presentation." },
|
||||
{ "id": "map.moving_object.has_state_facet", "from": ["map.moving_object"], "to": ["map.state_facet"], "status": "product-required", "summary": "A moving object exposes only the state dimensions explicitly declared by its bound Data Product and ontology value contracts." },
|
||||
{ "id": "map.style_profile.classifies_state_facet", "from": ["map.style_profile"], "to": ["map.state_facet"], "status": "product-required", "summary": "A versioned Foundry style profile maps normalized state facets to renderer-neutral presentation classes, filter groups and sort order." },
|
||||
{ "id": "map.subject.has_label", "from": ["map.place_target", "map.moving_object", "map.station", "map.stop", "map.terminal"], "to": ["map.label"], "status": "product-required", "summary": "Spatial subjects can use the common label contract and semantic size variants." },
|
||||
{ "id": "map.zone.uses_style_profile", "from": ["map.zone"], "to": ["map.style_profile"], "status": "product-required", "summary": "Zones use semantic fill, outline, height and level-dependent styling." },
|
||||
{ "id": "map.route.contains_track_segment", "from": ["map.route"], "to": ["map.track_segment"], "status": "product-required", "summary": "A logical route is rendered from one or more track segments." },
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Package: `catalog/domain-packages/gelios`
|
||||
|
||||
Status: `v1.0.0`, source-evidenced and product-required.
|
||||
Status: `v1.1.0`, source-evidenced and product-required.
|
||||
|
||||
## Purpose and boundary
|
||||
|
||||
@@ -15,8 +15,8 @@ model. Runtime transport, collection cadence and semantic mapping belong to an
|
||||
isolated NDC L2 connection instance. External Data Plane persists and delivers
|
||||
provider-neutral Data Products. Ontology Core describes meaning only.
|
||||
|
||||
The matching contract/data artifact is
|
||||
`platform/packages/external-provider-contract/providers/gelios/v1`. Adding a
|
||||
The current matching contract/data artifact is
|
||||
`platform/packages/external-provider-contract/providers/gelios/v4`. Adding a
|
||||
second account creates another connection instance with different opaque
|
||||
credential references; it does not create another ontology package, Platform
|
||||
service or custom node.
|
||||
@@ -24,14 +24,15 @@ service or custom node.
|
||||
## Provider authentication boundary
|
||||
|
||||
Gelios issues exactly two provider secret artifacts: an access token and a
|
||||
refresh token. The current NDC L2 `httpBearerAuth` request binding uses the
|
||||
access token. Automatic refresh has not been proven in the deployed runtime, so
|
||||
the matching provider package records refresh as `operator_managed`; neither
|
||||
token value belongs in Ontology, a workflow graph, MCP, Ops or a trace.
|
||||
refresh token. The current rotating credential uses the access token for
|
||||
requests and keeps refresh inside native NDC L2 Credentials; neither token
|
||||
value belongs in Ontology, a workflow graph, MCP, Ops or a trace.
|
||||
|
||||
A credential label such as `read access` is operator metadata, not a Gelios
|
||||
token scope. `gelios.units.current.read` is classified as read because the
|
||||
approved workflow transport is `GET /api/v1/units`. The label does not create a
|
||||
token scope. `gelios.units.current.read` and
|
||||
`gelios.monitoring_config.current.read` are classified as read because the
|
||||
approved transports are `GET /api/v1/units` and
|
||||
`GET /api/v1/users/me/monitoring-config`. The label does not create a
|
||||
separate read token or constrain other rights that Gelios may have granted to
|
||||
the same access token. The scoped Data Product writer credential later in the
|
||||
runtime path is an internal NDC/External Data Plane capability, not a third
|
||||
@@ -66,7 +67,7 @@ parsed or renumbered. A unit can use a
|
||||
expose sensor, fuel, maintenance and custom-field configurations.
|
||||
|
||||
Hardware IDs, IMEI, phones, address, decrypt-related fields, raw `params` and
|
||||
unclassified sensor payloads are excluded by the v1 field policy. New provider
|
||||
unclassified sensor payloads are excluded by the current field policy. New provider
|
||||
fields remain dropped until they are evidenced, classified and introduced by a
|
||||
new package/ontology revision.
|
||||
|
||||
@@ -76,12 +77,12 @@ new package/ontology revision.
|
||||
`gelios.position_fix` is its time-qualified spatial portion; it is not a pin or
|
||||
other renderer object.
|
||||
|
||||
The first approved output is exactly:
|
||||
The current approved output is exactly:
|
||||
|
||||
```text
|
||||
Data Product: fleet.positions.current.v1
|
||||
version: 1.0.0
|
||||
ontology revision: ontology.map.moving_object.v1
|
||||
Data Product: fleet.positions.current.v3
|
||||
version: 3.0.0
|
||||
ontology revision: ontology.map.moving_object.v3
|
||||
semantic type: map.moving_object
|
||||
delivery: snapshot+patch
|
||||
history: sampled, latest entity per 60-second bucket, 90 days
|
||||
@@ -96,21 +97,51 @@ elevation_meters
|
||||
geometry
|
||||
hdop
|
||||
horizontal_accuracy_meters
|
||||
movement_state
|
||||
object_kind
|
||||
operational_status
|
||||
position_source
|
||||
position_valid
|
||||
quality_flags
|
||||
satellite_count
|
||||
signal_state
|
||||
speed_kph
|
||||
```
|
||||
|
||||
All attribute names are snake_case. GeoJSON `geometry` is omitted when valid
|
||||
coordinates are unavailable, but the unit remains in the product with
|
||||
`position_valid=false`, `operational_status=no_position` and appropriate
|
||||
`quality_flags`. If the provider has no last-message timestamp, the collection
|
||||
receive time becomes the explicit `observedAt` fallback so the credential-visible
|
||||
unit is not silently dropped.
|
||||
coordinates are unavailable, but the unit remains in the product. Missing
|
||||
geometry is not converted into a Gelios monitoring status. If the provider has
|
||||
no last-message timestamp, the collection receive time becomes the explicit
|
||||
`observedAt` fallback so the credential-visible unit is not silently dropped.
|
||||
|
||||
### Official monitoring states
|
||||
|
||||
Gelios does not return a ready-made operational-status enum from
|
||||
`GET /api/v1/units`. Its monitoring client derives two independent, closed
|
||||
value contracts from official source facts and settings:
|
||||
|
||||
```text
|
||||
signal_state:
|
||||
active -> На связи / Active units
|
||||
inactive -> Не на связи / Inactive units
|
||||
|
||||
movement_state:
|
||||
moving -> В движении / Moving units
|
||||
stopped -> Неподвижные / Stopped units
|
||||
```
|
||||
|
||||
The official client first marks a unit active while the age of `lastMsg.time`
|
||||
is below `monitoring-config.signalActiveDuration`, then keeps it active through
|
||||
the positive `signalSomewhatInactiveDuration` window. A missing message is
|
||||
inactive. When an account returns empty monitoring durations, the connection
|
||||
profile may supply an explicit, versioned positive fallback; its provenance is
|
||||
runtime policy and does not create another ontology state. Without a resolved
|
||||
positive threshold publication must fail closed. `movement_state=moving` when
|
||||
the integer `lastMsg.speed` is greater than `2`; otherwise it is `stopped`.
|
||||
The values and Russian labels are the official Gelios monitoring-client
|
||||
contract.
|
||||
|
||||
There is no `unknown`, `fresh`, `stale`, GPS-quality, position-quality,
|
||||
`parked`, `no_position` or aggregate `operational_status` value in this
|
||||
contract. `gelios.operational_status` is retained in the catalog only as a
|
||||
`tech-debt-noncanonical` marker for retiring old v1/v2 products.
|
||||
|
||||
### Sensors and operational semantics
|
||||
|
||||
@@ -128,7 +159,7 @@ Gelios is a source domain. The provider-neutral relation is:
|
||||
```text
|
||||
gelios.unit + gelios.position_fix
|
||||
-> map.moving_object
|
||||
-> fleet.positions.current.v1
|
||||
-> fleet.positions.current.v3
|
||||
-> Foundry Data Product binding
|
||||
-> map layers, selection and telemetry panel
|
||||
```
|
||||
@@ -148,7 +179,7 @@ concepts. `gelios.command_dispatch`, `gelios.command_delivery` and
|
||||
No collection run, workflow, map click or autonomous agent may create a
|
||||
dispatch. A future command path requires explicit human intent, confirmation,
|
||||
separate authorization, immutable audit and delivery reconciliation. This
|
||||
ontology and the Gelios v1 provider package expose no command transport.
|
||||
ontology and the Gelios v4 provider package expose no command transport.
|
||||
|
||||
## Canonical runtime flow
|
||||
|
||||
@@ -182,6 +213,9 @@ physical storage.
|
||||
temporary credential issuance, configuration mutation and downloads remain
|
||||
excluded until separately classified.
|
||||
- Dynamic provider fields are dropped until evidenced and classified.
|
||||
- Gelios monitoring filters use only the exact `signal_state` and
|
||||
`movement_state` value contracts. L2, Data Products and Foundry cannot add
|
||||
fallback state values.
|
||||
- Geozones and history require bounded loading, paging/cursors, retention and
|
||||
volume controls.
|
||||
- Command send and mutation capabilities remain red even when provider access
|
||||
|
||||
@@ -20,12 +20,11 @@
|
||||
"elevation_meters": 0,
|
||||
"hdop": 0.8,
|
||||
"horizontal_accuracy_meters": 4.2,
|
||||
"movement_state": "stopped",
|
||||
"object_kind": "tracked_unit",
|
||||
"operational_status": "active",
|
||||
"position_source": "gelios",
|
||||
"position_valid": true,
|
||||
"quality_flags": [],
|
||||
"satellite_count": 11,
|
||||
"signal_state": "active",
|
||||
"speed_kph": 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,6 +393,20 @@ function safeEntity(value) {
|
||||
status: stringList(value?.status),
|
||||
authority: cleanString(value?.authority, 240),
|
||||
summary: cleanString(value?.summary, 2000),
|
||||
...(value?.valueContract ? { valueContract: safeValueContract(value.valueContract) } : {}),
|
||||
}
|
||||
}
|
||||
|
||||
function safeValueContract(value) {
|
||||
return {
|
||||
field: cleanString(value?.field, 120),
|
||||
values: (Array.isArray(value?.values) ? value.values : []).map((item) => ({
|
||||
value: cleanString(item?.value, 120),
|
||||
label: cleanString(item?.label, 240),
|
||||
sourceLabel: cleanString(item?.sourceLabel, 240),
|
||||
})),
|
||||
sourcePaths: stringList(value?.sourcePaths),
|
||||
rules: stringList(value?.rules),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@ try {
|
||||
|
||||
const search = await rpc(baseUrl, TOKEN, 3, 'tools/call', {
|
||||
name: 'ontology_search',
|
||||
arguments: { query: 'трайк', limit: 10 },
|
||||
arguments: { query: 'юнит гелиос', limit: 10 },
|
||||
})
|
||||
assert.equal(search.result.isError, undefined)
|
||||
assert.equal(search.result.structuredContent.query, 'трайк')
|
||||
assert.equal(search.result.structuredContent.query, 'юнит гелиос')
|
||||
assert.equal(search.result.structuredContent.results.some((item) => item.entity?.id === 'gelios.unit'), true)
|
||||
|
||||
const entity = await rpc(baseUrl, TOKEN, 4, 'tools/call', {
|
||||
@@ -50,6 +50,16 @@ try {
|
||||
assert.equal(entity.result.structuredContent.entity.id, 'gelios.integration')
|
||||
assert.equal(JSON.stringify(entity.result.structuredContent).includes('/Users/'), false)
|
||||
|
||||
const signalState = await rpc(baseUrl, TOKEN, 41, 'tools/call', {
|
||||
name: 'ontology_get_entity',
|
||||
arguments: { entityId: 'gelios.signal_state' },
|
||||
})
|
||||
assert.equal(signalState.result.structuredContent.entity.valueContract.field, 'signal_state')
|
||||
assert.deepEqual(
|
||||
signalState.result.structuredContent.entity.valueContract.values.map((item) => item.value),
|
||||
['active', 'inactive'],
|
||||
)
|
||||
|
||||
const guardrails = await rpc(baseUrl, TOKEN, 5, 'tools/call', {
|
||||
name: 'ontology_get_guardrails',
|
||||
arguments: { entityId: 'gelios.command_dispatch' },
|
||||
@@ -70,6 +80,7 @@ try {
|
||||
'mcp_initialize',
|
||||
'read_only_tool_catalog',
|
||||
'gelios_alias_resolution',
|
||||
'gelios_value_contract_visible',
|
||||
'gelios_command_guardrail_visible',
|
||||
'evidence_paths_not_exposed',
|
||||
'internal_bearer_required',
|
||||
|
||||
@@ -5,6 +5,7 @@ import { loadCatalog, serviceRoot } from './catalog.mjs'
|
||||
const ID_RE = /^[a-z][a-z0-9]*(?:\.[a-z][a-z0-9_]*)+$/
|
||||
const RULE_ID_RE = /^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)+$/
|
||||
const ROLE_ID_RE = /^[a-z][a-z0-9_]*$/
|
||||
const FIELD_ID_RE = /^[a-z][a-z0-9_]*$/
|
||||
const ALLOWED_RELATION_STATUSES = new Set([
|
||||
'source-confirmed',
|
||||
'source-evidenced',
|
||||
@@ -92,6 +93,7 @@ export async function validateCatalog() {
|
||||
for (const status of entity.status || []) {
|
||||
assert(statusVocabulary.has(status), `entity ${entity.id} uses unknown status: ${status}`, errors)
|
||||
}
|
||||
validateValueContract(entity.valueContract, entity.id, errors)
|
||||
}
|
||||
|
||||
assertUnique(relations.relations.map((relation) => relation.id), 'relation', errors)
|
||||
@@ -354,6 +356,32 @@ export async function validateCatalog() {
|
||||
}
|
||||
}
|
||||
|
||||
function validateValueContract(valueContract, entityId, errors) {
|
||||
if (valueContract === undefined) return
|
||||
const label = `entity ${entityId}.valueContract`
|
||||
assert(valueContract && typeof valueContract === 'object' && !Array.isArray(valueContract), `${label} must be object`, errors)
|
||||
if (!valueContract || typeof valueContract !== 'object' || Array.isArray(valueContract)) return
|
||||
assert(FIELD_ID_RE.test(valueContract.field || ''), `${label}.field invalid`, errors)
|
||||
assert(Array.isArray(valueContract.values) && valueContract.values.length > 0, `${label}.values must be non-empty array`, errors)
|
||||
const values = []
|
||||
for (const [index, item] of (valueContract.values || []).entries()) {
|
||||
const itemLabel = `${label}.values[${index}]`
|
||||
assert(item && typeof item === 'object' && !Array.isArray(item), `${itemLabel} must be object`, errors)
|
||||
if (!item || typeof item !== 'object' || Array.isArray(item)) continue
|
||||
assert(FIELD_ID_RE.test(item.value || ''), `${itemLabel}.value invalid`, errors)
|
||||
assert(typeof item.label === 'string' && item.label.trim(), `${itemLabel}.label required`, errors)
|
||||
assert(typeof item.sourceLabel === 'string' && item.sourceLabel.trim(), `${itemLabel}.sourceLabel required`, errors)
|
||||
values.push(item.value)
|
||||
}
|
||||
assert(new Set(values).size === values.length, `${label}.values duplicate value`, errors)
|
||||
for (const key of ['sourcePaths', 'rules']) {
|
||||
assert(Array.isArray(valueContract[key]) && valueContract[key].length > 0, `${label}.${key} must be non-empty array`, errors)
|
||||
for (const item of valueContract[key] || []) {
|
||||
assert(typeof item === 'string' && item.trim(), `${label}.${key} contains empty item`, errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
const result = await validateCatalog()
|
||||
if (!result.ok) {
|
||||
|
||||
Reference in New Issue
Block a user