{
  "schema": "webftr-js8-decoder-lab-browser-report-v1-slim-step90",
  "version": "step90-real-wav-intake-validator-auto-handoff",
  "created_utc": "2026-05-28T06:50:04Z",
  "project": "WebFTR JS8 Decoder Lab",
  "root": "/decoders/js8_decoder",
  "expected_root": "/decoders/js8_decoder",
  "logs_dir": "/decoders/js8_decoder/logs",
  "runtime_dir": "/decoders/js8_decoder/runtime",
  "report_mode": "step90_slim_timeout_guard",
  "daemon_status": {
    "schema": "webftr-js8lab-daemon-status-v2",
    "version": "step90-real-wav-intake-validator-auto-handoff",
    "created_utc": "2026-05-26T07:46:12Z",
    "updated_utc": "2026-05-28T06:39:01Z",
    "state": "diagnostics_ok",
    "detail": "Diagnose fertig",
    "exit_code": 0,
    "root": "/decoders/js8_decoder",
    "expected_root": "/decoders/js8_decoder",
    "wav": "/decoders/js8_test.wav",
    "mode": "real-wav-intake-validator-auto-handoff",
    "start_guard": {
      "start_stops_existing_instance": true,
      "stale_pid_cleanup": true,
      "root_orphan_cleanup": true,
      "port_guard": true,
      "foreign_port_owner_not_killed": true
    },
    "server": {
      "host": "0.0.0.0",
      "port": 8000,
      "pid": 125983,
      "running": true,
      "log": "/decoders/js8_decoder/logs/js8_browser_server.log"
    },
    "diagnostics": {
      "pid": 126001,
      "running": false,
      "log": "/decoders/js8_decoder/logs/js8_start_diagnostics_latest.log",
      "last_command": "./run_js8_decoder_lab.sh real-wav-intake-validator-auto-handoff /decoders/js8_test.wav"
    },
    "latest_chain_summary": {
      "available": true,
      "path": "/decoders/js8_decoder/logs/js8_real_wav_intake_validator_auto_handoff_latest.json",
      "stat": {
        "exists": true,
        "size": 6486,
        "mtime_utc": "2026-05-28T06:39:00Z"
      },
      "tool_version": "step90-real-wav-intake-validator-auto-handoff",
      "created_utc": "2026-05-28T06:39:00Z",
      "verdict": "step90_waiting_for_valid_additional_real_js8_freetext_wav",
      "planned_wav_count": 1,
      "data_frame_candidate_count": 0,
      "compressed_data_frame_candidate_count": 0,
      "extra_wav_count": 0,
      "valid_extra_wav_count": 0,
      "invalid_wav_count": 0,
      "invalid_extra_wav_count": 0,
      "primary_wav_valid": true,
      "validation_results": [
        {
          "path": "/decoders/js8_test.wav",
          "source": "primary",
          "stat": {
            "path": "/decoders/js8_test.wav",
            "exists": true,
            "is_file": true,
            "size": 4608442,
            "mtime_utc": "2026-05-26T06:05:27Z"
          },
          "exists": true,
          "is_file": true,
          "readable_wav": true,
          "valid_for_corpus_hunt": true,
          "warnings": [],
          "errors": [],
          "sha256_first_2m": "c049995adf22be86b9acd8d57a566fba642f877fbcd24c1c120463a665956fbf",
          "channels": 1,
          "sample_width_bytes": 2,
          "sample_rate_hz": 12000,
          "frames": 2304199,
          "duration_seconds": 192.017,
          "compression_type": "NONE"
        }
      ],
      "valid_extra_wavs": [],
      "invalid_wavs": [],
      "discovered_wav_count": 1,
      "latest_step87_summary": {
        "path": "/decoders/js8_decoder/logs/js8_real_wav_corpus_data_frame_hunt_latest.json",
        "stat": {
          "path": "/decoders/js8_decoder/logs/js8_real_wav_corpus_data_frame_hunt_latest.json",
          "exists": true,
          "is_file": true,
          "size": 7303,
          "mtime_utc": "2026-05-28T05:20:57Z"
        },
        "available": true,
        "tool_version": "step87-real-wav-corpus-data-frame-hunt",
        "created_utc": "2026-05-28T05:20:57Z",
        "verdict": "step87_corpus_control_frames_only_no_data_frames",
        "tested_wav_count": 1,
        "planned_wav_count": 1,
        "data_frame_candidate_count": 0,
        "compressed_data_frame_candidate_count": 0,
        "unique_control_frame_total_across_wavs": 2
      },
      "previous_step89_summary": {
        "path": "/decoders/js8_decoder/logs/js8_real_wav_corpus_auto_handoff_latest.json",
        "stat": {
          "path": "/decoders/js8_decoder/logs/js8_real_wav_corpus_auto_handoff_latest.json",
          "exists": true,
          "is_file": true,
          "size": 4026,
          "mtime_utc": "2026-05-28T06:14:28Z"
        },
        "available": true,
        "tool_version": "step89-real-wav-corpus-auto-handoff",
        "created_utc": "2026-05-28T06:14:28Z",
        "verdict": "step89_waiting_for_additional_real_js8_freetext_wav",
        "action": "wait_for_extra_wav",
        "full_hunt_ran": false,
        "data_frame_candidate_count": 0,
        "compressed_data_frame_candidate_count": 0
      },
      "previous_step90_summary": {
        "path": "/decoders/js8_decoder/logs/js8_real_wav_intake_validator_auto_handoff_latest.json",
        "stat": {
          "path": "/decoders/js8_decoder/logs/js8_real_wav_intake_validator_auto_handoff_latest.json",
          "exists": true,
          "is_file": true,
          "size": 6408,
          "mtime_utc": "2026-05-28T06:38:59Z"
        },
        "available": true,
        "tool_version": "step90-real-wav-intake-validator-auto-handoff",
        "created_utc": "2026-05-28T06:38:59Z",
        "verdict": "step90_waiting_for_valid_additional_real_js8_freetext_wav",
        "action": "wait_for_extra_wav",
        "full_hunt_ran": false,
        "valid_extra_wav_count": 0,
        "invalid_wav_count": 0,
        "data_frame_candidate_count": 0,
        "compressed_data_frame_candidate_count": 0
      },
      "operator_files": {
        "input_wavs_dir": "/decoders/js8_decoder/runtime/input_wavs",
        "fixtures_wavs_dir": "/decoders/js8_decoder/runtime/fixtures/wavs",
        "readme": "/decoders/js8_decoder/runtime/input_wavs/README_STEP88_WAV_CORPUS_INTAKE.md",
        "helper": "/decoders/js8_decoder/runtime/input_wavs/add_wav_to_corpus_step88.sh",
        "readme_stat": {
          "path": "/decoders/js8_decoder/runtime/input_wavs/README_STEP88_WAV_CORPUS_INTAKE.md",
          "exists": true,
          "is_file": true,
          "size": 427,
          "mtime_utc": "2026-05-28T06:38:59Z"
        },
        "helper_stat": {
          "path": "/decoders/js8_decoder/runtime/input_wavs/add_wav_to_corpus_step88.sh",
          "exists": true,
          "is_file": true,
          "size": 446,
          "mtime_utc": "2026-05-28T06:38:59Z"
        }
      },
      "step90_operator_files": {
        "input_wavs_dir": "/decoders/js8_decoder/runtime/input_wavs",
        "readme": "/decoders/js8_decoder/runtime/input_wavs/README_STEP90_WAV_INTAKE_VALIDATOR.md",
        "helper": "/decoders/js8_decoder/runtime/input_wavs/validate_wav_corpus_step90.sh",
        "readme_stat": {
          "path": "/decoders/js8_decoder/runtime/input_wavs/README_STEP90_WAV_INTAKE_VALIDATOR.md",
          "exists": true,
          "is_file": true,
          "size": 415,
          "mtime_utc": "2026-05-28T06:38:59Z"
        },
        "helper_stat": {
          "path": "/decoders/js8_decoder/runtime/input_wavs/validate_wav_corpus_step90.sh",
          "exists": true,
          "is_file": true,
          "size": 194,
          "mtime_utc": "2026-05-28T06:38:59Z"
        }
      },
      "action": "wait_for_extra_wav",
      "full_hunt_ran": false,
      "next_action": "Add a real JS8 WAV with free-text/Data/Compressed transmission to runtime/input_wavs or set JS8LAB_WAV_CORPUS_DIRS. Step90 validates the WAV first and then auto-runs the full corpus hunt only when a valid extra WAV is present. If Data/Compressed candidates appear, route them to the guarded release gate."
    },
    "links": {
      "home": "/",
      "api_report": "/api/report",
      "api_status": "/api/status",
      "logs": "/logs/",
      "latest_log": "/logs/latest.log",
      "start_diagnostics_log": "/logs/js8_start_diagnostics_latest.log",
      "server_log": "/logs/js8_browser_server.log"
    },
    "rx_only_guard": {
      "tx": false,
      "ptt": false,
      "tune": false,
      "send": false,
      "js8call_runtime_control": false
    },
    "history": [
      {
        "utc": "2026-05-28T03:18:40Z",
        "state": "diagnostics_running",
        "detail": "Diagnosejob im Hintergrund gestartet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T03:19:11Z",
        "state": "diagnostics_ok",
        "detail": "Diagnose fertig",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T03:38:58Z",
        "state": "restarting",
        "detail": "Start-Guard: bestehende JS8Lab-Instanz wird geprüft und beendet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T03:38:58Z",
        "state": "server_starting",
        "detail": "Browser-Logserver startet auf 0.0.0.0:8000",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T03:38:59Z",
        "state": "server_running",
        "detail": "Browser-Logserver läuft auf 0.0.0.0:8000",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T03:38:59Z",
        "state": "diagnostics_running",
        "detail": "Diagnose läuft: real-wav-message174-regeneration /decoders/js8_test.wav",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T03:39:01Z",
        "state": "diagnostics_running",
        "detail": "Diagnosejob im Hintergrund gestartet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T03:41:40Z",
        "state": "diagnostics_ok",
        "detail": "Diagnose fertig",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T03:53:37Z",
        "state": "restarting",
        "detail": "Start-Guard: bestehende JS8Lab-Instanz wird geprüft und beendet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T03:53:37Z",
        "state": "server_starting",
        "detail": "Browser-Logserver startet auf 0.0.0.0:8000",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T03:53:39Z",
        "state": "server_running",
        "detail": "Browser-Logserver läuft auf 0.0.0.0:8000",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T03:53:39Z",
        "state": "diagnostics_running",
        "detail": "Diagnose läuft: real-wav-status-surface-regression /decoders/js8_test.wav",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T03:53:40Z",
        "state": "diagnostics_running",
        "detail": "Diagnosejob im Hintergrund gestartet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T03:56:11Z",
        "state": "diagnostics_ok",
        "detail": "Diagnose fertig",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T04:10:53Z",
        "state": "restarting",
        "detail": "Start-Guard: bestehende JS8Lab-Instanz wird geprüft und beendet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T04:10:53Z",
        "state": "server_starting",
        "detail": "Browser-Logserver startet auf 0.0.0.0:8000",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T04:10:54Z",
        "state": "server_running",
        "detail": "Browser-Logserver läuft auf 0.0.0.0:8000",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T04:10:54Z",
        "state": "diagnostics_running",
        "detail": "Diagnose läuft: real-wav-wide-data-frame-search /decoders/js8_test.wav",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T04:10:55Z",
        "state": "diagnostics_running",
        "detail": "Diagnosejob im Hintergrund gestartet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T04:18:43Z",
        "state": "diagnostics_ok",
        "detail": "Diagnose fertig",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T04:36:38Z",
        "state": "restarting",
        "detail": "Start-Guard: bestehende JS8Lab-Instanz wird geprüft und beendet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T04:36:38Z",
        "state": "server_starting",
        "detail": "Browser-Logserver startet auf 0.0.0.0:8000",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T04:36:39Z",
        "state": "server_running",
        "detail": "Browser-Logserver läuft auf 0.0.0.0:8000",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T04:36:40Z",
        "state": "diagnostics_running",
        "detail": "Diagnose läuft: real-wav-unique-frame-census /decoders/js8_test.wav",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T04:36:41Z",
        "state": "diagnostics_running",
        "detail": "Diagnosejob im Hintergrund gestartet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T04:44:08Z",
        "state": "diagnostics_ok",
        "detail": "Diagnose fertig",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T05:11:47Z",
        "state": "restarting",
        "detail": "Start-Guard: bestehende JS8Lab-Instanz wird geprüft und beendet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T05:11:47Z",
        "state": "server_starting",
        "detail": "Browser-Logserver startet auf 0.0.0.0:8000",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T05:11:48Z",
        "state": "server_running",
        "detail": "Browser-Logserver läuft auf 0.0.0.0:8000",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T05:11:48Z",
        "state": "diagnostics_running",
        "detail": "Diagnose läuft: real-wav-corpus-data-frame-hunt /decoders/js8_test.wav",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T05:11:50Z",
        "state": "diagnostics_running",
        "detail": "Diagnosejob im Hintergrund gestartet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T05:21:01Z",
        "state": "diagnostics_ok",
        "detail": "Diagnose fertig",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T05:48:36Z",
        "state": "restarting",
        "detail": "Start-Guard: bestehende JS8Lab-Instanz wird geprüft und beendet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T05:48:37Z",
        "state": "server_starting",
        "detail": "Browser-Logserver startet auf 0.0.0.0:8000",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T05:48:38Z",
        "state": "server_running",
        "detail": "Browser-Logserver läuft auf 0.0.0.0:8000",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T05:48:38Z",
        "state": "diagnostics_running",
        "detail": "Diagnose läuft: real-wav-corpus-intake-status-slim /decoders/js8_test.wav",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T05:48:39Z",
        "state": "diagnostics_running",
        "detail": "Diagnosejob im Hintergrund gestartet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T05:48:39Z",
        "state": "diagnostics_ok",
        "detail": "Diagnose fertig",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T06:14:26Z",
        "state": "restarting",
        "detail": "Start-Guard: bestehende JS8Lab-Instanz wird geprüft und beendet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T06:14:26Z",
        "state": "server_starting",
        "detail": "Browser-Logserver startet auf 0.0.0.0:8000",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T06:14:27Z",
        "state": "server_running",
        "detail": "Browser-Logserver läuft auf 0.0.0.0:8000",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T06:14:27Z",
        "state": "diagnostics_running",
        "detail": "Diagnose läuft: real-wav-corpus-auto-handoff /decoders/js8_test.wav",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T06:14:29Z",
        "state": "diagnostics_running",
        "detail": "Diagnosejob im Hintergrund gestartet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T06:14:29Z",
        "state": "diagnostics_ok",
        "detail": "Diagnose fertig",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T06:38:57Z",
        "state": "restarting",
        "detail": "Start-Guard: bestehende JS8Lab-Instanz wird geprüft und beendet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T06:38:58Z",
        "state": "server_starting",
        "detail": "Browser-Logserver startet auf 0.0.0.0:8000",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T06:38:59Z",
        "state": "server_running",
        "detail": "Browser-Logserver läuft auf 0.0.0.0:8000",
        "exit_code": 0
      },
      {
        "utc": "2026-05-28T06:38:59Z",
        "state": "diagnostics_running",
        "detail": "Diagnose läuft: real-wav-intake-validator-auto-handoff /decoders/js8_test.wav",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T06:39:00Z",
        "state": "diagnostics_running",
        "detail": "Diagnosejob im Hintergrund gestartet",
        "exit_code": null
      },
      {
        "utc": "2026-05-28T06:39:01Z",
        "state": "diagnostics_ok",
        "detail": "Diagnose fertig",
        "exit_code": 0
      }
    ],
    "status_file": "/decoders/js8_decoder/runtime/js8lab_status.json"
  },
  "latest_chain_summary": {
    "available": true,
    "path": "/decoders/js8_decoder/logs/js8_real_wav_intake_validator_auto_handoff_latest.json",
    "stat": {
      "exists": true,
      "is_file": true,
      "is_dir": false,
      "size": 6486,
      "mtime_utc": "2026-05-28T06:39:00Z"
    },
    "tool_version": "step90-real-wav-intake-validator-auto-handoff",
    "created_utc": "2026-05-28T06:39:00Z",
    "verdict": "step90_waiting_for_valid_additional_real_js8_freetext_wav",
    "planned_wav_count": 1,
    "data_frame_candidate_count": 0,
    "compressed_data_frame_candidate_count": 0,
    "extra_wav_count": 0,
    "valid_extra_wav_count": 0,
    "invalid_wav_count": 0,
    "invalid_extra_wav_count": 0,
    "primary_wav_valid": true,
    "validation_results": [
      {
        "path": "/decoders/js8_test.wav",
        "source": "primary",
        "stat": {
          "path": "/decoders/js8_test.wav",
          "exists": true,
          "is_file": true,
          "size": 4608442,
          "mtime_utc": "2026-05-26T06:05:27Z"
        },
        "exists": true,
        "is_file": true,
        "readable_wav": true,
        "valid_for_corpus_hunt": true,
        "warnings": [],
        "errors": [],
        "sha256_first_2m": "c049995adf22be86b9acd8d57a566fba642f877fbcd24c1c120463a665956fbf",
        "channels": 1,
        "sample_width_bytes": 2,
        "sample_rate_hz": 12000,
        "frames": 2304199,
        "duration_seconds": 192.017,
        "compression_type": "NONE"
      }
    ],
    "valid_extra_wavs": [],
    "invalid_wavs": [],
    "discovered_wav_count": 1,
    "latest_step87_summary": {
      "path": "/decoders/js8_decoder/logs/js8_real_wav_corpus_data_frame_hunt_latest.json",
      "stat": {
        "path": "/decoders/js8_decoder/logs/js8_real_wav_corpus_data_frame_hunt_latest.json",
        "exists": true,
        "is_file": true,
        "size": 7303,
        "mtime_utc": "2026-05-28T05:20:57Z"
      },
      "available": true,
      "tool_version": "step87-real-wav-corpus-data-frame-hunt",
      "created_utc": "2026-05-28T05:20:57Z",
      "verdict": "step87_corpus_control_frames_only_no_data_frames",
      "tested_wav_count": 1,
      "planned_wav_count": 1,
      "data_frame_candidate_count": 0,
      "compressed_data_frame_candidate_count": 0,
      "unique_control_frame_total_across_wavs": 2
    },
    "previous_step89_summary": {
      "path": "/decoders/js8_decoder/logs/js8_real_wav_corpus_auto_handoff_latest.json",
      "stat": {
        "path": "/decoders/js8_decoder/logs/js8_real_wav_corpus_auto_handoff_latest.json",
        "exists": true,
        "is_file": true,
        "size": 4026,
        "mtime_utc": "2026-05-28T06:14:28Z"
      },
      "available": true,
      "tool_version": "step89-real-wav-corpus-auto-handoff",
      "created_utc": "2026-05-28T06:14:28Z",
      "verdict": "step89_waiting_for_additional_real_js8_freetext_wav",
      "action": "wait_for_extra_wav",
      "full_hunt_ran": false,
      "data_frame_candidate_count": 0,
      "compressed_data_frame_candidate_count": 0
    },
    "previous_step90_summary": {
      "path": "/decoders/js8_decoder/logs/js8_real_wav_intake_validator_auto_handoff_latest.json",
      "stat": {
        "path": "/decoders/js8_decoder/logs/js8_real_wav_intake_validator_auto_handoff_latest.json",
        "exists": true,
        "is_file": true,
        "size": 6408,
        "mtime_utc": "2026-05-28T06:38:59Z"
      },
      "available": true,
      "tool_version": "step90-real-wav-intake-validator-auto-handoff",
      "created_utc": "2026-05-28T06:38:59Z",
      "verdict": "step90_waiting_for_valid_additional_real_js8_freetext_wav",
      "action": "wait_for_extra_wav",
      "full_hunt_ran": false,
      "valid_extra_wav_count": 0,
      "invalid_wav_count": 0,
      "data_frame_candidate_count": 0,
      "compressed_data_frame_candidate_count": 0
    },
    "operator_files": {
      "input_wavs_dir": "/decoders/js8_decoder/runtime/input_wavs",
      "fixtures_wavs_dir": "/decoders/js8_decoder/runtime/fixtures/wavs",
      "readme": "/decoders/js8_decoder/runtime/input_wavs/README_STEP88_WAV_CORPUS_INTAKE.md",
      "helper": "/decoders/js8_decoder/runtime/input_wavs/add_wav_to_corpus_step88.sh",
      "readme_stat": {
        "path": "/decoders/js8_decoder/runtime/input_wavs/README_STEP88_WAV_CORPUS_INTAKE.md",
        "exists": true,
        "is_file": true,
        "size": 427,
        "mtime_utc": "2026-05-28T06:38:59Z"
      },
      "helper_stat": {
        "path": "/decoders/js8_decoder/runtime/input_wavs/add_wav_to_corpus_step88.sh",
        "exists": true,
        "is_file": true,
        "size": 446,
        "mtime_utc": "2026-05-28T06:38:59Z"
      }
    },
    "step90_operator_files": {
      "input_wavs_dir": "/decoders/js8_decoder/runtime/input_wavs",
      "readme": "/decoders/js8_decoder/runtime/input_wavs/README_STEP90_WAV_INTAKE_VALIDATOR.md",
      "helper": "/decoders/js8_decoder/runtime/input_wavs/validate_wav_corpus_step90.sh",
      "readme_stat": {
        "path": "/decoders/js8_decoder/runtime/input_wavs/README_STEP90_WAV_INTAKE_VALIDATOR.md",
        "exists": true,
        "is_file": true,
        "size": 415,
        "mtime_utc": "2026-05-28T06:38:59Z"
      },
      "helper_stat": {
        "path": "/decoders/js8_decoder/runtime/input_wavs/validate_wav_corpus_step90.sh",
        "exists": true,
        "is_file": true,
        "size": 194,
        "mtime_utc": "2026-05-28T06:38:59Z"
      }
    },
    "action": "wait_for_extra_wav",
    "full_hunt_ran": false,
    "next_action": "Add a real JS8 WAV with free-text/Data/Compressed transmission to runtime/input_wavs or set JS8LAB_WAV_CORPUS_DIRS. Step90 validates the WAV first and then auto-runs the full corpus hunt only when a valid extra WAV is present. If Data/Compressed candidates appear, route them to the guarded release gate."
  },
  "wav_candidates": [
    {
      "path": "/decoders/js8_test.wav",
      "exists": true,
      "is_file": true,
      "is_dir": false,
      "size": 4608442,
      "mtime_utc": "2026-05-26T06:05:27Z"
    },
    {
      "path": "/home/webftr/decoders/js8_test.wav",
      "exists": false,
      "error": "[Errno 2] No such file or directory: '/home/webftr/decoders/js8_test.wav'"
    },
    {
      "path": "/decoders/js8_decoder/runtime/input_wavs",
      "exists": true,
      "is_file": false,
      "is_dir": true,
      "size": 4096,
      "mtime_utc": "2026-05-28T06:38:59Z"
    },
    {
      "path": "/decoders/js8_decoder/runtime/fixtures/wavs",
      "exists": true,
      "is_file": false,
      "is_dir": true,
      "size": 4096,
      "mtime_utc": "2026-05-28T05:48:38Z"
    }
  ],
  "selected_wav_hint": {
    "path": "/decoders/js8_test.wav",
    "exists": true,
    "is_file": true,
    "is_dir": false,
    "size": 4608442,
    "mtime_utc": "2026-05-26T06:05:27Z"
  },
  "latest_manifest": {
    "name": "20260528T063859Z_real-wav-intake-validator-auto-handoff_manifest.json",
    "url": "/logs/20260528T063859Z_real-wav-intake-validator-auto-handoff_manifest.json",
    "stat": {
      "exists": true,
      "is_file": true,
      "is_dir": false,
      "size": 1721,
      "mtime_utc": "2026-05-28T06:39:00Z"
    },
    "json": {
      "schema": "webftr-js8-decoder-lab-log-manifest-v2",
      "outputs": {
        "real_wav_intake_validator_precheck_json": "/decoders/js8_decoder/logs/20260528T063859Z_real_wav_intake_validator_auto_handoff_precheck_output.json",
        "real_wav_intake_validator_json": "/decoders/js8_decoder/logs/20260528T063859Z_real_wav_intake_validator_auto_handoff_output.json",
        "real_wav_intake_validator_latest_json": "/decoders/js8_decoder/logs/js8_real_wav_intake_validator_auto_handoff_latest.json",
        "latest_step87_json": "/decoders/js8_decoder/logs/js8_real_wav_corpus_data_frame_hunt_latest.json"
      }
    }
  },
  "latest_log": {
    "url": "/logs/latest.log",
    "stat": {
      "exists": true,
      "is_file": true,
      "is_dir": false,
      "size": 14741,
      "mtime_utc": "2026-05-28T06:39:00Z"
    },
    "tail": "0,\n  \"valid_extra_wav_count\": 0,\n  \"invalid_wav_count\": 0,\n  \"invalid_extra_wav_count\": 0,\n  \"primary_wav_valid\": true,\n  \"planned_wavs\": [\n    {\n      \"path\": \"/decoders/js8_test.wav\",\n      \"source\": \"primary\",\n      \"stat\": {\n        \"path\": \"/decoders/js8_test.wav\",\n        \"exists\": true,\n        \"is_file\": true,\n        \"size\": 4608442,\n        \"mtime_utc\": \"2026-05-26T06:05:27Z\"\n      }\n    }\n  ],\n  \"validation_results\": [\n    {\n      \"path\": \"/decoders/js8_test.wav\",\n      \"source\": \"primary\",\n      \"stat\": {\n        \"path\": \"/decoders/js8_test.wav\",\n        \"exists\": true,\n        \"is_file\": true,\n        \"size\": 4608442,\n        \"mtime_utc\": \"2026-05-26T06:05:27Z\"\n      },\n      \"exists\": true,\n      \"is_file\": true,\n      \"readable_wav\": true,\n      \"valid_for_corpus_hunt\": true,\n      \"warnings\": [],\n      \"errors\": [],\n      \"sha256_first_2m\": \"c049995adf22be86b9acd8d57a566fba642f877fbcd24c1c120463a665956fbf\",\n      \"channels\": 1,\n      \"sample_width_bytes\": 2,\n      \"sample_rate_hz\": 12000,\n      \"frames\": 2304199,\n      \"duration_seconds\": 192.017,\n      \"compression_type\": \"NONE\"\n    }\n  ],\n  \"valid_extra_wavs\": [],\n  \"invalid_wavs\": [],\n  \"latest_step87_summary\": {\n    \"path\": \"/decoders/js8_decoder/logs/js8_real_wav_corpus_data_frame_hunt_latest.json\",\n    \"stat\": {\n      \"path\": \"/decoders/js8_decoder/logs/js8_real_wav_corpus_data_frame_hunt_latest.json\",\n      \"exists\": true,\n      \"is_file\": true,\n      \"size\": 7303,\n      \"mtime_utc\": \"2026-05-28T05:20:57Z\"\n    },\n    \"available\": true,\n    \"tool_version\": \"step87-real-wav-corpus-data-frame-hunt\",\n    \"created_utc\": \"2026-05-28T05:20:57Z\",\n    \"verdict\": \"step87_corpus_control_frames_only_no_data_frames\",\n    \"tested_wav_count\": 1,\n    \"planned_wav_count\": 1,\n    \"data_frame_candidate_count\": 0,\n    \"compressed_data_frame_candidate_count\": 0,\n    \"unique_control_frame_total_across_wavs\": 2\n  },\n  \"previous_step89_summary\": {\n    \"path\": \"/decoders/js8_decoder/logs/js8_real_wav_corpus_auto_handoff_latest.json\",\n    \"stat\": {\n      \"path\": \"/decoders/js8_decoder/logs/js8_real_wav_corpus_auto_handoff_latest.json\",\n      \"exists\": true,\n      \"is_file\": true,\n      \"size\": 4026,\n      \"mtime_utc\": \"2026-05-28T06:14:28Z\"\n    },\n    \"available\": true,\n    \"tool_version\": \"step89-real-wav-corpus-auto-handoff\",\n    \"created_utc\": \"2026-05-28T06:14:28Z\",\n    \"verdict\": \"step89_waiting_for_additional_real_js8_freetext_wav\",\n    \"action\": \"wait_for_extra_wav\",\n    \"full_hunt_ran\": false,\n    \"data_frame_candidate_count\": 0,\n    \"compressed_data_frame_candidate_count\": 0\n  },\n  \"previous_step90_summary\": {\n    \"path\": \"/decoders/js8_decoder/logs/js8_real_wav_intake_validator_auto_handoff_latest.json\",\n    \"stat\": {\n      \"path\": \"/decoders/js8_decoder/logs/js8_real_wav_intake_validator_auto_handoff_latest.json\",\n      \"exists\": true,\n      \"is_file\": true,\n      \"size\": 6408,\n      \"mtime_utc\": \"2026-05-28T06:38:59Z\"\n    },\n    \"available\": true,\n    \"tool_version\": \"step90-real-wav-intake-validator-auto-handoff\",\n    \"created_utc\": \"2026-05-28T06:38:59Z\",\n    \"verdict\": \"step90_waiting_for_valid_additional_real_js8_freetext_wav\",\n    \"action\": \"wait_for_extra_wav\",\n    \"full_hunt_ran\": false,\n    \"valid_extra_wav_count\": 0,\n    \"invalid_wav_count\": 0,\n    \"data_frame_candidate_count\": 0,\n    \"compressed_data_frame_candidate_count\": 0\n  },\n  \"operator_files\": {\n    \"input_wavs_dir\": \"/decoders/js8_decoder/runtime/input_wavs\",\n    \"fixtures_wavs_dir\": \"/decoders/js8_decoder/runtime/fixtures/wavs\",\n    \"readme\": \"/decoders/js8_decoder/runtime/input_wavs/README_STEP88_WAV_CORPUS_INTAKE.md\",\n    \"helper\": \"/decoders/js8_decoder/runtime/input_wavs/add_wav_to_corpus_step88.sh\",\n    \"readme_stat\": {\n      \"path\": \"/decoders/js8_decoder/runtime/input_wavs/README_STEP88_WAV_CORPUS_INTAKE.md\",\n      \"exists\": true,\n      \"is_file\": true,\n      \"size\": 427,\n      \"mtime_utc\": \"2026-05-28T06:38:59Z\"\n    },\n    \"helper_stat\": {\n      \"path\": \"/decoders/js8_decoder/runtime/input_wavs/add_wav_to_corpus_step88.sh\",\n      \"exists\": true,\n      \"is_file\": true,\n      \"size\": 446,\n      \"mtime_utc\": \"2026-05-28T06:38:59Z\"\n    }\n  },\n  \"step90_operator_files\": {\n    \"input_wavs_dir\": \"/decoders/js8_decoder/runtime/input_wavs\",\n    \"readme\": \"/decoders/js8_decoder/runtime/input_wavs/README_STEP90_WAV_INTAKE_VALIDATOR.md\",\n    \"helper\": \"/decoders/js8_decoder/runtime/input_wavs/validate_wav_corpus_step90.sh\",\n    \"readme_stat\": {\n      \"path\": \"/decoders/js8_decoder/runtime/input_wavs/README_STEP90_WAV_INTAKE_VALIDATOR.md\",\n      \"exists\": true,\n      \"is_file\": true,\n      \"size\": 415,\n      \"mtime_utc\": \"2026-05-28T06:38:59Z\"\n    },\n    \"helper_stat\": {\n      \"path\": \"/decoders/js8_decoder/runtime/input_wavs/validate_wav_corpus_step90.sh\",\n      \"exists\": true,\n      \"is_file\": true,\n      \"size\": 194,\n      \"mtime_utc\": \"2026-05-28T06:38:59Z\"\n    }\n  },\n  \"after_hunt\": false,\n  \"force_hunt\": false,\n  \"full_hunt_ran\": false,\n  \"data_frame_candidate_count\": 0,\n  \"compressed_data_frame_candidate_count\": 0,\n  \"action\": \"wait_for_extra_wav\",\n  \"verdict\": \"step90_waiting_for_valid_additional_real_js8_freetext_wav\",\n  \"warnings\": [\n    \"Step90 still does not release JS8 free text directly.\",\n    \"The primary /decoders/js8_test.wav is a control-frame reference and is not counted as an extra WAV.\",\n    \"Only valid extra WAVs trigger the full Step87 corpus hunt automatically.\"\n  ],\n  \"next_action\": \"Add a real JS8 WAV with free-text/Data/Compressed transmission to runtime/input_wavs or set JS8LAB_WAV_CORPUS_DIRS. Step90 validates the WAV first and then auto-runs the full corpus hunt only when a valid extra WAV is present. If Data/Compressed candidates appear, route them to the guarded release gate.\"\n}\n\n[webftr-js8-lab] OK\n[webftr-js8-lab] log file: /decoders/js8_decoder/logs/20260528T063859Z_real-wav-intake-validator-auto-handoff.log\n[webftr-js8-lab] manifest: /decoders/js8_decoder/logs/20260528T063859Z_real-wav-intake-validator-auto-handoff_manifest.json\n"
  },
  "recent_files": [
    {
      "name": "js8_browser_server.log",
      "size": 515,
      "mtime_utc": "2026-05-28T06:50:03Z",
      "url": "/logs/js8_browser_server.log"
    },
    {
      "name": "js8_browser_report_latest.json",
      "size": 42173,
      "mtime_utc": "2026-05-28T06:50:03Z",
      "url": "/logs/js8_browser_report_latest.json"
    },
    {
      "name": "js8_start_diagnostics_latest.log",
      "size": 15108,
      "mtime_utc": "2026-05-28T06:39:00Z",
      "url": "/logs/js8_start_diagnostics_latest.log"
    },
    {
      "name": "20260528T063859Z_real-wav-intake-validator-auto-handoff.log",
      "size": 14741,
      "mtime_utc": "2026-05-28T06:39:00Z",
      "url": "/logs/20260528T063859Z_real-wav-intake-validator-auto-handoff.log"
    },
    {
      "name": "latest.log",
      "size": 14741,
      "mtime_utc": "2026-05-28T06:39:00Z",
      "url": "/logs/latest.log"
    },
    {
      "name": "20260528T063859Z_real-wav-intake-validator-auto-handoff_manifest.json",
      "size": 1721,
      "mtime_utc": "2026-05-28T06:39:00Z",
      "url": "/logs/20260528T063859Z_real-wav-intake-validator-auto-handoff_manifest.json"
    },
    {
      "name": "js8_real_wav_intake_validator_auto_handoff_latest.json",
      "size": 6486,
      "mtime_utc": "2026-05-28T06:39:00Z",
      "url": "/logs/js8_real_wav_intake_validator_auto_handoff_latest.json"
    },
    {
      "name": "20260528T063859Z_real_wav_intake_validator_auto_handoff_output.json",
      "size": 6486,
      "mtime_utc": "2026-05-28T06:39:00Z",
      "url": "/logs/20260528T063859Z_real_wav_intake_validator_auto_handoff_output.json"
    },
    {
      "name": "20260528T063859Z_real_wav_intake_validator_auto_handoff_output.stderr.log",
      "size": 0,
      "mtime_utc": "2026-05-28T06:38:59Z",
      "url": "/logs/20260528T063859Z_real_wav_intake_validator_auto_handoff_output.stderr.log"
    },
    {
      "name": "20260528T063859Z_real_wav_intake_validator_auto_handoff_precheck_output.json",
      "size": 6409,
      "mtime_utc": "2026-05-28T06:38:59Z",
      "url": "/logs/20260528T063859Z_real_wav_intake_validator_auto_handoff_precheck_output.json"
    },
    {
      "name": "20260528T063859Z_real_wav_intake_validator_auto_handoff_precheck_output.stderr.log",
      "size": 0,
      "mtime_utc": "2026-05-28T06:38:59Z",
      "url": "/logs/20260528T063859Z_real_wav_intake_validator_auto_handoff_precheck_output.stderr.log"
    },
    {
      "name": "20260528T061427Z_real-wav-corpus-auto-handoff.log",
      "size": 9657,
      "mtime_utc": "2026-05-28T06:14:29Z",
      "url": "/logs/20260528T061427Z_real-wav-corpus-auto-handoff.log"
    },
    {
      "name": "20260528T061427Z_real-wav-corpus-auto-handoff_manifest.json",
      "size": 1663,
      "mtime_utc": "2026-05-28T06:14:29Z",
      "url": "/logs/20260528T061427Z_real-wav-corpus-auto-handoff_manifest.json"
    },
    {
      "name": "js8_real_wav_corpus_auto_handoff_latest.json",
      "size": 4026,
      "mtime_utc": "2026-05-28T06:14:28Z",
      "url": "/logs/js8_real_wav_corpus_auto_handoff_latest.json"
    },
    {
      "name": "20260528T061427Z_real_wav_corpus_auto_handoff_output.json",
      "size": 4026,
      "mtime_utc": "2026-05-28T06:14:28Z",
      "url": "/logs/20260528T061427Z_real_wav_corpus_auto_handoff_output.json"
    },
    {
      "name": "20260528T061427Z_real_wav_corpus_auto_handoff_output.stderr.log",
      "size": 0,
      "mtime_utc": "2026-05-28T06:14:28Z",
      "url": "/logs/20260528T061427Z_real_wav_corpus_auto_handoff_output.stderr.log"
    },
    {
      "name": "20260528T061427Z_real_wav_corpus_auto_handoff_precheck_output.json",
      "size": 3955,
      "mtime_utc": "2026-05-28T06:14:28Z",
      "url": "/logs/20260528T061427Z_real_wav_corpus_auto_handoff_precheck_output.json"
    },
    {
      "name": "20260528T061427Z_real_wav_corpus_auto_handoff_precheck_output.stderr.log",
      "size": 0,
      "mtime_utc": "2026-05-28T06:14:27Z",
      "url": "/logs/20260528T061427Z_real_wav_corpus_auto_handoff_precheck_output.stderr.log"
    },
    {
      "name": "20260528T054838Z_real-wav-corpus-intake-status-slim.log",
      "size": 4561,
      "mtime_utc": "2026-05-28T05:48:39Z",
      "url": "/logs/20260528T054838Z_real-wav-corpus-intake-status-slim.log"
    },
    {
      "name": "20260528T054838Z_real-wav-corpus-intake-status-slim_manifest.json",
      "size": 1460,
      "mtime_utc": "2026-05-28T05:48:39Z",
      "url": "/logs/20260528T054838Z_real-wav-corpus-intake-status-slim_manifest.json"
    },
    {
      "name": "js8_real_wav_corpus_intake_status_latest.json",
      "size": 3273,
      "mtime_utc": "2026-05-28T05:48:38Z",
      "url": "/logs/js8_real_wav_corpus_intake_status_latest.json"
    },
    {
      "name": "20260528T054838Z_real_wav_corpus_intake_status_output.json",
      "size": 3273,
      "mtime_utc": "2026-05-28T05:48:38Z",
      "url": "/logs/20260528T054838Z_real_wav_corpus_intake_status_output.json"
    },
    {
      "name": "20260528T054838Z_real_wav_corpus_intake_status_output.stderr.log",
      "size": 0,
      "mtime_utc": "2026-05-28T05:48:38Z",
      "url": "/logs/20260528T054838Z_real_wav_corpus_intake_status_output.stderr.log"
    },
    {
      "name": "20260528T051148Z_real-wav-corpus-data-frame-hunt.log",
      "size": 1372528,
      "mtime_utc": "2026-05-28T05:21:01Z",
      "url": "/logs/20260528T051148Z_real-wav-corpus-data-frame-hunt.log"
    },
    {
      "name": "20260528T051148Z_real-wav-corpus-data-frame-hunt_manifest.json",
      "size": 1740,
      "mtime_utc": "2026-05-28T05:20:59Z",
      "url": "/logs/20260528T051148Z_real-wav-corpus-data-frame-hunt_manifest.json"
    },
    {
      "name": "js8_real_wav_corpus_data_frame_hunt_latest.json",
      "size": 7303,
      "mtime_utc": "2026-05-28T05:20:57Z",
      "url": "/logs/js8_real_wav_corpus_data_frame_hunt_latest.json"
    },
    {
      "name": "20260528T051148Z_real_wav_corpus_data_frame_hunt_output.json",
      "size": 7303,
      "mtime_utc": "2026-05-28T05:20:57Z",
      "url": "/logs/20260528T051148Z_real_wav_corpus_data_frame_hunt_output.json"
    },
    {
      "name": "20260528T051148Z_real_wav_corpus_data_frame_hunt_output.stderr.log",
      "size": 0,
      "mtime_utc": "2026-05-28T05:20:57Z",
      "url": "/logs/20260528T051148Z_real_wav_corpus_data_frame_hunt_output.stderr.log"
    },
    {
      "name": "20260528T051148Z_step87_result_jsons.txt",
      "size": 88,
      "mtime_utc": "2026-05-28T05:20:57Z",
      "url": "/logs/20260528T051148Z_step87_result_jsons.txt"
    },
    {
      "name": "20260528T051148Z_step87_wav0_unique_frame_census_output.json",
      "size": 24463,
      "mtime_utc": "2026-05-28T05:20:57Z",
      "url": "/logs/20260528T051148Z_step87_wav0_unique_frame_census_output.json"
    },
    {
      "name": "20260528T051149Z_real-wav-unique-frame-census.log",
      "size": 1361350,
      "mtime_utc": "2026-05-28T05:20:57Z",
      "url": "/logs/20260528T051149Z_real-wav-unique-frame-census.log"
    },
    {
      "name": "20260528T051149Z_real-wav-unique-frame-census_manifest.json",
      "size": 1522,
      "mtime_utc": "2026-05-28T05:20:55Z",
      "url": "/logs/20260528T051149Z_real-wav-unique-frame-census_manifest.json"
    },
    {
      "name": "js8_real_wav_unique_frame_census_latest.json",
      "size": 24463,
      "mtime_utc": "2026-05-28T05:20:53Z",
      "url": "/logs/js8_real_wav_unique_frame_census_latest.json"
    },
    {
      "name": "20260528T051149Z_real_wav_unique_frame_census_output.json",
      "size": 24463,
      "mtime_utc": "2026-05-28T05:20:53Z",
      "url": "/logs/20260528T051149Z_real_wav_unique_frame_census_output.json"
    },
    {
      "name": "20260528T051149Z_real_wav_unique_frame_census_output.stderr.log",
      "size": 0,
      "mtime_utc": "2026-05-28T05:20:53Z",
      "url": "/logs/20260528T051149Z_real_wav_unique_frame_census_output.stderr.log"
    },
    {
      "name": "20260528T051150Z_real-wav-wide-data-frame-search.log",
      "size": 1335427,
      "mtime_utc": "2026-05-28T05:20:53Z",
      "url": "/logs/20260528T051150Z_real-wav-wide-data-frame-search.log"
    },
    {
      "name": "20260528T051150Z_real-wav-wide-data-frame-search_manifest.json",
      "size": 1778,
      "mtime_utc": "2026-05-28T05:20:52Z",
      "url": "/logs/20260528T051150Z_real-wav-wide-data-frame-search_manifest.json"
    },
    {
      "name": "js8_step85_window_results_manifest_latest.json",
      "size": 4220,
      "mtime_utc": "2026-05-28T05:20:50Z",
      "url": "/logs/js8_step85_window_results_manifest_latest.json"
    },
    {
      "name": "js8_real_wav_wide_window_plan_latest.json",
      "size": 11377,
      "mtime_utc": "2026-05-28T05:20:50Z",
      "url": "/logs/js8_real_wav_wide_window_plan_latest.json"
    },
    {
      "name": "js8_real_wav_wide_data_frame_search_latest.json",
      "size": 32464,
      "mtime_utc": "2026-05-28T05:20:50Z",
      "url": "/logs/js8_real_wav_wide_data_frame_search_latest.json"
    }
  ],
  "stable_latest_links": {
    "browser_report": "/logs/js8_browser_report_latest.json",
    "real_wav_corpus_auto_handoff_latest": "/logs/js8_real_wav_corpus_auto_handoff_latest.json",
    "real_wav_corpus_intake_status_latest": "/logs/js8_real_wav_corpus_intake_status_latest.json",
    "real_wav_corpus_data_frame_hunt_latest": "/logs/js8_real_wav_corpus_data_frame_hunt_latest.json",
    "real_wav_corpus_plan_latest": "/logs/js8_real_wav_corpus_plan_latest.json",
    "real_wav_unique_frame_census_latest": "/logs/js8_real_wav_unique_frame_census_latest.json",
    "latest_log": "/logs/latest.log"
  },
  "browser_links": {
    "home": "/",
    "api_report": "/api/report",
    "api_status": "/api/status",
    "logs": "/logs/"
  },
  "rx_only_guard": {
    "tx": false,
    "ptt": false,
    "tune": false,
    "send": false,
    "js8call_runtime_control": false,
    "webftr_productive_integration": false
  },
  "note": "Step89/Step88 slim report avoids heavy historical JSON previews so /api/report and /logs stay responsive. Full files remain directly downloadable under /logs/<filename>."
}