{
  "ok": true,
  "tool": "webftr-js8-webftr-rx-ui-contract-export",
  "tool_version": "step68-webftr-rx-ui-contract-export",
  "schema": "webftr-js8-rx-ui-contract-v1",
  "rx_only_guard": {
    "tx": false,
    "ptt": false,
    "tune": false,
    "send": false,
    "js8call_runtime_control": false,
    "webftr_productive_integration": false
  },
  "no_gui_runtime_started": true,
  "root": "/decoders/js8_decoder",
  "log_dir": "/decoders/js8_decoder/logs",
  "input_reports": [
    {
      "path": "/decoders/js8_decoder/logs/20260527T180708Z_source_confirmed_control_frame_rx_release_output.json",
      "exists": true,
      "selected_input": true,
      "input_kind": "step67_source_confirmed_control_frame_rx_release"
    }
  ],
  "source_step67_version": "step67-source-confirmed-control-frame-rx-release",
  "source_webftr_display_count": 2,
  "source_hidden_candidate_count": 0,
  "webftr_rx_ui_count": 2,
  "webftr_rx_rows": [
    {
      "id": "js8-ui68-80c34aaa298675",
      "source_row_id": "js8-rx67-de15d3110b0d",
      "mode": "JS8",
      "protocol": "JS8",
      "decoder": "JS8Lab Step68 guarded control-frame contract",
      "row_type": "control_frame",
      "frame_type": "FrameHeartbeat",
      "status": "HB",
      "from": "004REY/0V4",
      "to": "@HB",
      "grid": "OJ16",
      "command": "HB",
      "message": "HB 004REY/0V4 OJ16",
      "text": "HB 004REY/0V4 OJ16",
      "display_text": "HB 004REY/0V4 OJ16",
      "raw_message174": "001XqOA2iDZ0",
      "valid_message174_crc12": true,
      "crc12_distance": 0,
      "received_crc12": 2920,
      "computed_crc12": 2920,
      "confidence": "high",
      "confidence_score": 6,
      "confidence_reasons": [
        "valid_heartbeat_callsign",
        "valid_heartbeat_grid"
      ],
      "show_in_rx_list": true,
      "ui_badge": "HB",
      "ui_title": "HB 004REY/0V4 OJ16",
      "ui_subtitle": "004REY/0V4 → @HB OJ16",
      "sort_index": 0,
      "rx_only": true,
      "source": {
        "source_file": "/decoders/js8_decoder/logs/20260527T150600Z_source_exact_map_probe_output.json",
        "json_path": "$.best_candidate.candidate_bits",
        "source_kind": "generic_bit_list",
        "row_index": 237,
        "map_name": "raw",
        "map_source": null
      },
      "release_guard": "step67_source_confirmed_fixed_control_frame_only",
      "candidate_class": "webftr_control_frame_display_ready"
    },
    {
      "id": "js8-ui68-50f4e1e7052c17",
      "source_row_id": "js8-rx67-bd37f68199eb",
      "mode": "JS8",
      "protocol": "JS8",
      "decoder": "JS8Lab Step68 guarded control-frame contract",
      "row_type": "control_frame",
      "frame_type": "FrameDirected",
      "status": "INFO",
      "from": "9I2TZR/P",
      "to": "0Z0PMP",
      "grid": "",
      "command": "INFO",
      "message": "INFO 28",
      "text": "9I2TZR/P 0Z0PMP INFO 28",
      "display_text": "9I2TZR/P 0Z0PMP INFO 28",
      "raw_message174": "Q0VsKWqfJ96x",
      "valid_message174_crc12": true,
      "crc12_distance": 0,
      "received_crc12": 980,
      "computed_crc12": 980,
      "confidence": "high",
      "confidence_score": 10,
      "confidence_reasons": [
        "valid_directed_from",
        "valid_directed_to",
        "directed_command"
      ],
      "show_in_rx_list": true,
      "ui_badge": "INFO",
      "ui_title": "9I2TZR/P 0Z0PMP INFO 28",
      "ui_subtitle": "9I2TZR/P → 0Z0PMP",
      "sort_index": 1,
      "rx_only": true,
      "source": {
        "source_file": "/decoders/js8_decoder/logs/20260527T150600Z_source_exact_map_probe_output.json",
        "json_path": "$.top_llr_row_exports[6].hard_bits",
        "source_kind": "generic_bit_list",
        "row_index": 82,
        "map_name": "raw",
        "map_source": null
      },
      "release_guard": "step67_source_confirmed_fixed_control_frame_only",
      "candidate_class": "webftr_control_frame_display_ready"
    }
  ],
  "ui_contract": {
    "polling_target": "logs/js8_webftr_rx_ui_contract_latest.json",
    "rows_key": "webftr_rx_rows",
    "display_fields": [
      "mode",
      "status",
      "from",
      "to",
      "grid",
      "message",
      "display_text",
      "confidence"
    ],
    "dedupe_key": "id",
    "sort_key": "sort_index",
    "current_scope": "source-confirmed fixed JS8 control frames only",
    "blocked_scope": "FrameData and FrameDataCompressed remain excluded until JSC::decompress and multi-frame assembly are source-ported."
  },
  "webftr_adapter_hint": {
    "safe_to_poll_read_only": true,
    "productive_integration": false,
    "suggested_module": "JS8 RX preview / diagnostics panel",
    "do_not_merge_as_chat_text_yet": true,
    "note": "This contract is stable enough for WebFTR display experiments, but it is still diagnostic and control-frame-only."
  },
  "suppressed_candidates": [],
  "source_release_decisions": [
    {
      "source_candidate_id": "js8-frame66-bf1ce62fdb4a",
      "raw_message174": "001XqOA2iDZ0",
      "frame_type": "FrameHeartbeat",
      "decision": "released_source_confirmed_control_frame",
      "sort_index": 0,
      "released_row_id": "js8-rx67-de15d3110b0d"
    },
    {
      "source_candidate_id": "js8-frame66-66ab29ded46c",
      "raw_message174": "Q0VsKWqfJ96x",
      "frame_type": "FrameDirected",
      "decision": "released_source_confirmed_control_frame",
      "sort_index": 1,
      "released_row_id": "js8-rx67-bd37f68199eb"
    }
  ],
  "verdict": "webftr_rx_ui_contract_ready_control_frames_only",
  "warnings": [
    "Step68 remains RX-only and does not start JS8Call GUI/Qt.",
    "Only Step67 source-confirmed fixed control frames are exported for UI display.",
    "Normal JS8 free text still needs JSC::decompress and multi-frame assembly before productive display."
  ],
  "next_action": "Use this contract as a read-only WebFTR JS8 RX preview source; next research step is source-porting JSC::decompress/multi-frame assembly for free text.",
  "stable_latest_written": "/decoders/js8_decoder/logs/js8_webftr_rx_ui_contract_latest.json"
}
