1
0
Files
homelab-public-context/runtime/latest.json
T
2026-09-08 05:35:24 +00:00

39 lines
16 KiB
JSON

{
"schema_version": 1,
"channel": "homelab-runtime",
"command_id": "SIGNALBOT-260908-RECONNECT-POLICY-RCA-037",
"status": "OK",
"rc": 0,
"host": "pve01",
"mode": "read-only",
"component": "signalbot-cr0116-reconnect-policy-rca",
"started_at_utc": "2026-09-08T05:35:22Z",
"finished_at_utc": "2026-09-08T05:35:24Z",
"reference_register_checked": true,
"reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66",
"error_register_checked": true,
"error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0",
"command_sha256": "692caa6f77d6852cd79ece2fe377b72066856f9f0c6b00e6fb1b177ab4e20643",
"duplicate_failed_command_blocked": false,
"block_reason": null,
"execution_started": true,
"change_declared": false,
"result_contract_valid": true,
"result_contract_status": null,
"result_contract_error": null,
"command_rc": 0,
"changes_made": false,
"rollback_started": false,
"rollback_restored": null,
"mutation_outcome": "NO_MUTATION",
"sanitized": true,
"secrets_included": false,
"private_addresses_included": false,
"raw_evidence_retained_locally": true,
"raw_evidence_sha256": "61489801dbaae6b142aa5eca683f0d2f1cc3f35c69fbabf9a5d70b44f37f85c0",
"sanitized_output_sha256": "61489801dbaae6b142aa5eca683f0d2f1cc3f35c69fbabf9a5d70b44f37f85c0",
"output_truncated_in_json": false,
"full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt",
"output": "ERROR_REGISTER_CHECK=OK\nREFERENCE_CHECK=OK\nAUTHORITY_CHECK_SCOPE=READABILITY_ONLY_INCIDENTS_NOT_CLOSED\nSB037_COLLECTOR={\"id\":\"a546a70c3e8c\",\"image\":\"8020-demonov-shadow:e32760c69a4311728db9066ee8bf2bf66b1e5a70\",\"name\":\"demonov-shadow-8020-collector-1\",\"ok\":true,\"pid\":210213,\"restart_count\":13,\"restart_policy\":{\"MaximumRetryCount\":0,\"Name\":\"unless-stopped\"},\"running\":true,\"selected_env\":{},\"started_at\":\"2026-09-08T05:01:08.70385245Z\",\"status\":\"running\"}\nSB037_RECONNECTS={\"activity\":{\"count\":180684,\"max_event_seq\":180684,\"max_received_at\":\"2026-09-08T05:35:16.819878+00:00\"},\"db_exists\":true,\"earliest_candidate_clean_d1_close_msk\":\"2026-09-10T03:00:00+00:00\",\"earliest_candidate_clean_d1_close_utc\":\"2026-09-10T00:00:00+00:00\",\"earliest_candidate_clean_d1_start_utc\":\"2026-09-09T00:00:00+00:00\",\"last_reconnect_at\":\"2026-09-08T05:01:09.298101+00:00\",\"quick_check\":\"ok\",\"reconnect_count\":10,\"reconnects\":[{\"event_seq\":16,\"received_at\":\"2026-09-06T18:48:31.906387+00:00\",\"session_hash\":\"184e0e63b1c9\",\"source_sequence\":1},{\"event_seq\":71022,\"received_at\":\"2026-09-07T09:50:41.075840+00:00\",\"session_hash\":\"8da25a454977\",\"source_sequence\":1},{\"event_seq\":85452,\"received_at\":\"2026-09-07T13:30:08.973280+00:00\",\"session_hash\":\"a2db9de2102e\",\"source_sequence\":1},{\"event_seq\":95747,\"received_at\":\"2026-09-07T14:46:51.906734+00:00\",\"session_hash\":\"d1d81c259d86\",\"source_sequence\":1},{\"event_seq\":97435,\"received_at\":\"2026-09-07T15:04:59.188298+00:00\",\"session_hash\":\"69bb22240441\",\"source_sequence\":1},{\"event_seq\":169559,\"received_at\":\"2026-09-08T03:52:02.239825+00:00\",\"session_hash\":\"210de3583c8e\",\"source_sequence\":1},{\"event_seq\":173179,\"received_at\":\"2026-09-08T04:42:44.434580+00:00\",\"session_hash\":\"10709eb54a7b\",\"source_sequence\":1},{\"event_seq\":173768,\"received_at\":\"2026-09-08T04:43:46.442548+00:00\",\"session_hash\":\"b501ae120c1f\",\"source_sequence\":1},{\"event_seq\":174756,\"received_at\":\"2026-09-08T04:54:28.087186+00:00\",\"session_hash\":\"2b3998047f77\",\"source_sequence\":1},{\"event_seq\":177231,\"received_at\":\"2026-09-08T05:01:09.298101+00:00\",\"session_hash\":\"253ffe9a26a5\",\"source_sequence\":1}]}\nSB037_STATUS={\"age_sec\":6.4,\"exists\":true,\"values\":{\"fail_closed_reason\":\"source reconnect detected\",\"gap_detected\":false,\"post_finality_revision\":false,\"prospective_fail_closed_reason\":\"\",\"prospective_gap_detected\":false,\"prospective_reconnect_detected\":false,\"reconnect_detected\":true}}\nSB037_CODE={\"lines\":1246,\"matches\":[{\"match_line\":63,\"window\":[{\"line\":59,\"text\":\"\"},{\"line\":60,\"text\":\"@dataclass(frozen=True)\"},{\"line\":61,\"text\":\"class BitfinexCollectorStatus:\"},{\"line\":62,\"text\":\"gap_detected: bool\"},{\"line\":63,\"text\":\"reconnect_detected: bool\"},{\"line\":64,\"text\":\"post_finality_revision: bool\"},{\"line\":65,\"text\":\"fail_closed_reason: str\"},{\"line\":66,\"text\":\"\"},{\"line\":67,\"text\":\"\"}]},{\"match_line\":124,\"window\":[{\"line\":120,\"text\":\"collection_start_at,\"},{\"line\":121,\"text\":\"\\\"collection_start_at\\\",\"},{\"line\":122,\"text\":\")\"},{\"line\":123,\"text\":\"self.path.parent.mkdir(parents=True, exist_ok=True)\"},{\"line\":124,\"text\":\"self._db = sqlite3.connect(\"},{\"line\":125,\"text\":\"str(self.path),\"},{\"line\":126,\"text\":\"timeout=5,\"},{\"line\":127,\"text\":\"isolation_level=None,\"},{\"line\":128,\"text\":\")\"}]},{\"match_line\":247,\"window\":[{\"line\":243,\"text\":\"\"},{\"line\":244,\"text\":\"def _status(self) -> BitfinexCollectorStatus:\"},{\"line\":245,\"text\":\"return BitfinexCollectorStatus(\"},{\"line\":246,\"text\":\"gap_detected=self._meta_get(\\\"gap_detected\\\") == \\\"1\\\",\"},{\"line\":247,\"text\":\"reconnect_detected=self._meta_get(\\\"reconnect_detected\\\") == \\\"1\\\",\"},{\"line\":248,\"text\":\"post_finality_revision=(\"},{\"line\":249,\"text\":\"self._meta_get(\\\"post_finality_revision\\\") == \\\"1\\\"\"},{\"line\":250,\"text\":\"),\"},{\"line\":251,\"text\":\"fail_closed_reason=self._meta_get(\\\"fail_closed_reason\\\") or \\\"\\\",\"}]},{\"match_line\":259,\"window\":[{\"line\":255,\"text\":\"status = self._status()\"},{\"line\":256,\"text\":\"prospective = self.current_prospective_source_state()\"},{\"line\":257,\"text\":\"payload = {\"},{\"line\":258,\"text\":\"\\\"gap_detected\\\": status.gap_detected,\"},{\"line\":259,\"text\":\"\\\"reconnect_detected\\\": status.reconnect_detected,\"},{\"line\":260,\"text\":\"\\\"post_finality_revision\\\": status.post_finality_revision,\"},{\"line\":261,\"text\":\"\\\"fail_closed_reason\\\": status.fail_closed_reason,\"},{\"line\":262,\"text\":\"\\\"prospective_gap_detected\\\": prospective.gap_detected,\"},{\"line\":263,\"text\":\"\\\"prospective_reconnect_detected\\\": prospective.reconnect_detected,\"}]},{\"match_line\":263,\"window\":[{\"line\":259,\"text\":\"\\\"reconnect_detected\\\": status.reconnect_detected,\"},{\"line\":260,\"text\":\"\\\"post_finality_revision\\\": status.post_finality_revision,\"},{\"line\":261,\"text\":\"\\\"fail_closed_reason\\\": status.fail_closed_reason,\"},{\"line\":262,\"text\":\"\\\"prospective_gap_detected\\\": prospective.gap_detected,\"},{\"line\":263,\"text\":\"\\\"prospective_reconnect_detected\\\": prospective.reconnect_detected,\"},{\"line\":264,\"text\":\"\\\"prospective_fail_closed_reason\\\": prospective.fail_closed_reason,\"},{\"line\":265,\"text\":\"\\\"updated_at\\\": datetime.now(timezone.utc).isoformat(),\"},{\"line\":266,\"text\":\"}\"},{\"line\":267,\"text\":\"target = self.path.with_name(\\\"collector_status.json\\\")\"}]},{\"match_line\":348,\"window\":[{\"line\":344,\"text\":\")\"},{\"line\":345,\"text\":\"self._persist_status_file()\"},{\"line\":346,\"text\":\"return\"},{\"line\":347,\"text\":\"if active != session_id:\"},{\"line\":348,\"text\":\"self._meta_set(\\\"reconnect_detected\\\", \\\"1\\\")\"},{\"line\":349,\"text\":\"if not self._meta_get(\\\"fail_closed_reason\\\"):\"},{\"line\":350,\"text\":\"self._meta_set(\"},{\"line\":351,\"text\":\"\\\"fail_closed_reason\\\",\"},{\"line\":352,\"text\":\"\\\"source reconnect detected\\\",\"}]},{\"match_line\":352,\"window\":[{\"line\":348,\"text\":\"self._meta_set(\\\"reconnect_detected\\\", \\\"1\\\")\"},{\"line\":349,\"text\":\"if not self._meta_get(\\\"fail_closed_reason\\\"):\"},{\"line\":350,\"text\":\"self._meta_set(\"},{\"line\":351,\"text\":\"\\\"fail_closed_reason\\\",\"},{\"line\":352,\"text\":\"\\\"source reconnect detected\\\",\"},{\"line\":353,\"text\":\")\"},{\"line\":354,\"text\":\"self._meta_set(\\\"active_session_id\\\", session_id)\"},{\"line\":355,\"text\":\"self._meta_set(f\\\"last_sequence:{session_id}\\\", str(sequence))\"},{\"line\":356,\"text\":\"self._db.execute(\"}]},{\"match_line\":366,\"window\":[{\"line\":362,\"text\":\"(\"},{\"line\":363,\"text\":\"session_id,\"},{\"line\":364,\"text\":\"sequence,\"},{\"line\":365,\"text\":\"received_at.isoformat(),\"},{\"line\":366,\"text\":\"\\\"RECONNECT\\\",\"},{\"line\":367,\"text\":\"),\"},{\"line\":368,\"text\":\")\"},{\"line\":369,\"text\":\"self._persist_status_file()\"},{\"line\":370,\"text\":\"return\"}]},{\"match_line\":413,\"window\":[{\"line\":409,\"text\":\")\"},{\"line\":410,\"text\":\"self._persist_status_file()\"},{\"line\":411,\"text\":\"\"},{\"line\":412,\"text\":\"def current_prospective_source_state(self) -> BitfinexCollectorStatus:\"},{\"line\":413,\"text\":\"# GAP/RECONNECT are enforced per logical D1 by finalize_ready_d1().\"},{\"line\":414,\"text\":\"# Historical events must not poison later uncontaminated D1 bars.\"},{\"line\":415,\"text\":\"post_finality_revision = (\"},{\"line\":416,\"text\":\"self._meta_get(\\\"post_finality_revision\\\") == \\\"1\\\"\"},{\"line\":417,\"text\":\")\"}]},{\"match_line\":425,\"window\":[{\"line\":421,\"text\":\"else \\\"\\\"\"},{\"line\":422,\"text\":\")\"},{\"line\":423,\"text\":\"return BitfinexCollectorStatus(\"},{\"line\":424,\"text\":\"gap_detected=False,\"},{\"line\":425,\"text\":\"reconnect_detected=False,\"},{\"line\":426,\"text\":\"post_finality_revision=post_finality_revision,\"},{\"line\":427,\"text\":\"fail_closed_reason=reason,\"},{\"line\":428,\"text\":\")\"},{\"line\":429,\"text\":\"\"}]},{\"match_line\":605,\"window\":[{\"line\":601,\"text\":\"close=prices[-1],\"},{\"line\":602,\"text\":\"volume=volume,\"},{\"line\":603,\"text\":\")\"},{\"line\":604,\"text\":\"\"},{\"line\":605,\"text\":\"def finalize_ready_d1(\"},{\"line\":606,\"text\":\"self,\"},{\"line\":607,\"text\":\"logical_d1: LogicalD1V1,\"},{\"line\":608,\"text\":\"*,\"},{\"line\":609,\"text\":\"finalized_at: datetime,\"}]},{\"match_line\":666,\"window\":[{\"line\":662,\"text\":\"raise DemonovError(\"},{\"line\":663,\"text\":\"\\\"sequence gap prevents D1 finalization\\\"\"},{\"line\":664,\"text\":\")\"},{\"line\":665,\"text\":\"\"},{\"line\":666,\"text\":\"reconnect = self._db.execute(\"},{\"line\":667,\"text\":\"\\\"\\\"\\\"\"},{\"line\":668,\"text\":\"SELECT 1\"},{\"line\":669,\"text\":\"FROM sequence_events\"},{\"line\":670,\"text\":\"WHERE event_kind='RECONNECT'\"}]},{\"match_line\":670,\"window\":[{\"line\":666,\"text\":\"reconnect = self._db.execute(\"},{\"line\":667,\"text\":\"\\\"\\\"\\\"\"},{\"line\":668,\"text\":\"SELECT 1\"},{\"line\":669,\"text\":\"FROM sequence_events\"},{\"line\":670,\"text\":\"WHERE event_kind='RECONNECT'\"},{\"line\":671,\"text\":\"AND received_at>=?\"},{\"line\":672,\"text\":\"AND received_at<=?\"},{\"line\":673,\"text\":\"LIMIT 1\"},{\"line\":674,\"text\":\"\\\"\\\"\\\",\"}]},{\"match_line\":680,\"window\":[{\"line\":676,\"text\":\"logical_d1.open_at.isoformat(),\"},{\"line\":677,\"text\":\"boundary_received_at.isoformat(),\"},{\"line\":678,\"text\":\"),\"},{\"line\":679,\"text\":\").fetchone()\"},{\"line\":680,\"text\":\"if reconnect is not None:\"},{\"line\":681,\"text\":\"raise DemonovError(\"},{\"line\":682,\"text\":\"\\\"reconnect prevents D1 finalization\\\"\"},{\"line\":683,\"text\":\")\"},{\"line\":684,\"text\":\"\"}]},{\"match_line\":682,\"window\":[{\"line\":678,\"text\":\"),\"},{\"line\":679,\"text\":\").fetchone()\"},{\"line\":680,\"text\":\"if reconnect is not None:\"},{\"line\":681,\"text\":\"raise DemonovError(\"},{\"line\":682,\"text\":\"\\\"reconnect prevents D1 finalization\\\"\"},{\"line\":683,\"text\":\")\"},{\"line\":684,\"text\":\"\"},{\"line\":685,\"text\":\"revision = self.candidate_revision(logical_d1)\"},{\"line\":686,\"text\":\"if revision is None:\"}]},{\"match_line\":1102,\"window\":[{\"line\":1098,\"text\":\"finalized=finalized,\"},{\"line\":1099,\"text\":\")\"},{\"line\":1100,\"text\":\"\"},{\"line\":1101,\"text\":\"\"},{\"line\":1102,\"text\":\"def finalize_ready_d1_without_stopping_collection(\"},{\"line\":1103,\"text\":\"journal: SQLiteBitfinexSourceJournal,\"},{\"line\":1104,\"text\":\"logical_d1: LogicalD1V1,\"},{\"line\":1105,\"text\":\"*,\"},{\"line\":1106,\"text\":\"finalized_at: datetime,\"}]},{\"match_line\":1110,\"window\":[{\"line\":1106,\"text\":\"finalized_at: datetime,\"},{\"line\":1107,\"text\":\") -> FinalizedD1RecordV1 | None:\"},{\"line\":1108,\"text\":\"\\\"\\\"\\\"Keep collection alive when one logical D1 is non-finalizable.\\\"\\\"\\\"\"},{\"line\":1109,\"text\":\"try:\"},{\"line\":1110,\"text\":\"return journal.finalize_ready_d1(\"},{\"line\":1111,\"text\":\"logical_d1,\"},{\"line\":1112,\"text\":\"finalized_at=finalized_at,\"},{\"line\":1113,\"text\":\")\"},{\"line\":1114,\"text\":\"except DemonovError as exc:\"}]},{\"match_line\":1116,\"window\":[{\"line\":1112,\"text\":\"finalized_at=finalized_at,\"},{\"line\":1113,\"text\":\")\"},{\"line\":1114,\"text\":\"except DemonovError as exc:\"},{\"line\":1115,\"text\":\"if str(exc) in {\"},{\"line\":1116,\"text\":\"\\\"reconnect prevents D1 finalization\\\",\"},{\"line\":1117,\"text\":\"\\\"sequence gap prevents D1 finalization\\\",\"},{\"line\":1118,\"text\":\"}:\"},{\"line\":1119,\"text\":\"return None\"},{\"line\":1120,\"text\":\"raise\"}]},{\"match_line\":1135,\"window\":[{\"line\":1131,\"text\":\"\"},{\"line\":1132,\"text\":\"\"},{\"line\":1133,\"text\":\"def main() -> int:\"},{\"line\":1134,\"text\":\"try:\"},{\"line\":1135,\"text\":\"from websockets.sync.client import connect\"},{\"line\":1136,\"text\":\"except ImportError as exc:\"},{\"line\":1137,\"text\":\"raise DemonovError(\"},{\"line\":1138,\"text\":\"\\\"websockets runtime dependency is required\\\"\"},{\"line\":1139,\"text\":\") from exc\"}]},{\"match_line\":1138,\"window\":[{\"line\":1134,\"text\":\"try:\"},{\"line\":1135,\"text\":\"from websockets.sync.client import connect\"},{\"line\":1136,\"text\":\"except ImportError as exc:\"},{\"line\":1137,\"text\":\"raise DemonovError(\"},{\"line\":1138,\"text\":\"\\\"websockets runtime dependency is required\\\"\"},{\"line\":1139,\"text\":\") from exc\"},{\"line\":1140,\"text\":\"\"},{\"line\":1141,\"text\":\"data_dir = Path(os.environ.get(\\\"DATA_DIR\\\", \\\"/data\\\"))\"},{\"line\":1142,\"text\":\"path = data_dir / \\\"bitfinex_source.db\\\"\"}]},{\"match_line\":1153,\"window\":[{\"line\":1149,\"text\":\"with SQLiteBitfinexSourceJournal(\"},{\"line\":1150,\"text\":\"path,\"},{\"line\":1151,\"text\":\"collection_start_at=collection_start_at,\"},{\"line\":1152,\"text\":\") as journal:\"},{\"line\":1153,\"text\":\"with connect(\"},{\"line\":1154,\"text\":\"BITFINEX_WS_URL,\"},{\"line\":1155,\"text\":\"open_timeout=10,\"},{\"line\":1156,\"text\":\"close_timeout=5,\"},{\"line\":1157,\"text\":\") as ws:\"}]},{\"match_line\":1175,\"window\":[{\"line\":1171,\"text\":\"\\\"symbol\\\": BITFINEX_TRADE_SYMBOL,\"},{\"line\":1172,\"text\":\"}\"},{\"line\":1173,\"text\":\")\"},{\"line\":1174,\"text\":\")\"},{\"line\":1175,\"text\":\"for raw in ws:\"},{\"line\":1176,\"text\":\"message = json.loads(raw)\"},{\"line\":1177,\"text\":\"if isinstance(message, dict):\"},{\"line\":1178,\"text\":\"if (\"},{\"line\":1179,\"text\":\"message.get(\\\"event\\\") == \\\"subscribed\\\"\"}]},{\"match_line\":1237,\"window\":[{\"line\":1233,\"text\":\"open_at=open_at,\"},{\"line\":1234,\"text\":\"close_at=close_at,\"},{\"line\":1235,\"text\":\"finality_policy_id=\\\"BITFINEX_SEQ_ALL_D1_V1\\\",\"},{\"line\":1236,\"text\":\")\"},{\"line\":1237,\"text\":\"finalize_ready_d1_without_stopping_collection(\"},{\"line\":1238,\"text\":\"journal,\"},{\"line\":1239,\"text\":\"logical_d1,\"},{\"line\":1240,\"text\":\"finalized_at=received_at,\"},{\"line\":1241,\"text\":\")\"}]}],\"path\":\"/app/demonov_forward_map/bitfinex_trade_collector.py\",\"rc\":0,\"sha256\":\"d21cf0c3a0d4fcaf9210a57da04fd560dba4952ac1dd5a085abcc4e76ccf2eb0\",\"websockets_version\":\"15.0.1\"}\nSB037_DECISION=RECONNECT_POLICY_EVIDENCE_READY\nSB037_MUTATION_SCOPE=NONE_READ_ONLY\nSIGNALBOT037_DECISION=RECONNECT_POLICY_EVIDENCE_READY\nSIGNALBOT037_MUTATION_SCOPE=NO_RUNTIME_NO_GIT_NO_EXCHANGE_MUTATION\n"
}