From b3bcf6b8007b477e1ecdb5df54836244b1fe5c2c Mon Sep 17 00:00:00 2001 From: homelab-runtime-publisher Date: Tue, 8 Sep 2026 05:35:24 +0000 Subject: [PATCH] runtime: publish SIGNALBOT-260908-RECONNECT-POLICY-RCA-037 --- ...ALBOT-260908-RECONNECT-POLICY-RCA-037.json | 38 +++++++++++++++ ...NALBOT-260908-RECONNECT-POLICY-RCA-037.txt | 43 +++++++++++++++++ runtime/latest.json | 28 +++++------ runtime/latest.txt | 47 ++++++++----------- 4 files changed, 114 insertions(+), 42 deletions(-) create mode 100644 runtime/history/SIGNALBOT-260908-RECONNECT-POLICY-RCA-037.json create mode 100644 runtime/history/SIGNALBOT-260908-RECONNECT-POLICY-RCA-037.txt diff --git a/runtime/history/SIGNALBOT-260908-RECONNECT-POLICY-RCA-037.json b/runtime/history/SIGNALBOT-260908-RECONNECT-POLICY-RCA-037.json new file mode 100644 index 00000000..f615703a --- /dev/null +++ b/runtime/history/SIGNALBOT-260908-RECONNECT-POLICY-RCA-037.json @@ -0,0 +1,38 @@ +{ + "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" +} diff --git a/runtime/history/SIGNALBOT-260908-RECONNECT-POLICY-RCA-037.txt b/runtime/history/SIGNALBOT-260908-RECONNECT-POLICY-RCA-037.txt new file mode 100644 index 00000000..47c1a64f --- /dev/null +++ b/runtime/history/SIGNALBOT-260908-RECONNECT-POLICY-RCA-037.txt @@ -0,0 +1,43 @@ +CHAT_OUTPUT_BEGIN +COMMAND_ID=SIGNALBOT-260908-RECONNECT-POLICY-RCA-037 +STATUS=OK +RC=0 +HOST=pve01 +MODE=read-only +COMPONENT=signalbot-cr0116-reconnect-policy-rca +REFERENCE_REGISTER_CHECK=OK +REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 +ERROR_REGISTER_CHECK=OK +ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 +COMMAND_SHA256=692caa6f77d6852cd79ece2fe377b72066856f9f0c6b00e6fb1b177ab4e20643 +DUPLICATE_FAILED_COMMAND_BLOCKED=false +EXECUTION_STARTED=true +CHANGE_DECLARED=false +RESULT_CONTRACT_VALID=true +RESULT_CONTRACT_STATUS=NOT_APPLICABLE +RESULT_CONTRACT_ERROR=NONE +COMMAND_RC=0 +CHANGES_MADE=false +ROLLBACK_STARTED=false +ROLLBACK_RESTORED=null +MUTATION_OUTCOME=NO_MUTATION +SANITIZED=yes +SECRETS_INCLUDED=no +PRIVATE_ADDRESSES_INCLUDED=no +RAW_EVIDENCE_SHA256=61489801dbaae6b142aa5eca683f0d2f1cc3f35c69fbabf9a5d70b44f37f85c0 +SANITIZED_OUTPUT_SHA256=61489801dbaae6b142aa5eca683f0d2f1cc3f35c69fbabf9a5d70b44f37f85c0 +OUTPUT_BEGIN +ERROR_REGISTER_CHECK=OK +REFERENCE_CHECK=OK +AUTHORITY_CHECK_SCOPE=READABILITY_ONLY_INCIDENTS_NOT_CLOSED +SB037_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"} +SB037_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}]} +SB037_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}} +SB037_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"} +SB037_DECISION=RECONNECT_POLICY_EVIDENCE_READY +SB037_MUTATION_SCOPE=NONE_READ_ONLY +SIGNALBOT037_DECISION=RECONNECT_POLICY_EVIDENCE_READY +SIGNALBOT037_MUTATION_SCOPE=NO_RUNTIME_NO_GIT_NO_EXCHANGE_MUTATION + +OUTPUT_END +CHAT_OUTPUT_END diff --git a/runtime/latest.json b/runtime/latest.json index cfbdc5ac..f615703a 100644 --- a/runtime/latest.json +++ b/runtime/latest.json @@ -1,27 +1,27 @@ { "schema_version": 1, "channel": "homelab-runtime", - "command_id": "NEWFI-260908-A-STAGINGEDGE-APPLY1", - "status": "BLOCKED", - "rc": 2, + "command_id": "SIGNALBOT-260908-RECONNECT-POLICY-RCA-037", + "status": "OK", + "rc": 0, "host": "pve01", - "mode": "change", - "component": "newfi-staging-edge-route-tls-dns-apply", - "started_at_utc": "2026-09-08T05:30:56Z", - "finished_at_utc": "2026-09-08T05:31:42Z", + "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": "1aae3a98b115a56ea222e88ead3d1b124de0dfe6a609794df2a677715b31e9bf", + "command_sha256": "692caa6f77d6852cd79ece2fe377b72066856f9f0c6b00e6fb1b177ab4e20643", "duplicate_failed_command_blocked": false, "block_reason": null, "execution_started": true, - "change_declared": true, + "change_declared": false, "result_contract_valid": true, - "result_contract_status": "BLOCKED", + "result_contract_status": null, "result_contract_error": null, - "command_rc": 2, + "command_rc": 0, "changes_made": false, "rollback_started": false, "rollback_restored": null, @@ -30,9 +30,9 @@ "secrets_included": false, "private_addresses_included": false, "raw_evidence_retained_locally": true, - "raw_evidence_sha256": "feb4cd657a3eec33fa1821da8fd4d9baf104ed2ce7dc34b1a3250b708fba5e27", - "sanitized_output_sha256": "42c3d7d4c61a6e31cd08d373384189a95bc3aa5d36da9e2a429071e1eee04823", + "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": "STAGINGEDGE_APPLY_BEGIN=true\nCOMMAND_ID=NEWFI-260908-A-STAGINGEDGE-APPLY1\nCOMPONENT=newfi-staging-edge-route-tls-dns-apply\nMODE=change\nERROR_REGISTER_CHECK=OK\nERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0\nREFERENCE_CHECK=OK\nREFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66\nAUTHORITY_CHECK_SCOPE=READABILITY_ONLY_INCIDENTS_NOT_CLOSED\nRUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d\nPRIOR_APPLY4_HISTORY_SHA256=329f346c5385910120b68b4596f8057319a09e822290e5f70da50fd4d469904f\nPORTAL_PRODUCT_STATE_GATE=PASS\nNEWFI_STAGING_IDENTITY={\"name\":\"newfi-staging\",\"node\":\"pve03\",\"vmid\":211}\nSTAGING_BACKEND_PREFLIGHT={\"ip\":\"[PRIVATE_IP]\",\"local_http\":\"503\"}\nEDGE_VM_IDENTITY={\"name\":\"edge-vm\",\"node\":\"pve03\",\"vmid\":130}\nNPMPLUS_PREFLIGHT={\"container\":\"npmplus\",\"data_source\":\"/opt/npmplus\",\"nginx\":\"/usr/local/nginx/sbin/nginx\"}\nEDGE_CF_TOKEN_GATE={\"source_path\":\"/root/cert38-narrow-migration-20260904T074428Z/credentials-38.before\",\"valid_count\":1,\"zone_id_sha256\":\"5a88f12ebe16612b4e42f11ee57b4b95ca4a13c4abb8f0009f09babfbec927e7\"}\nSTAGINGEDGE_BLOCKED_REASON=NO_EXISTING_TLS_CERT_COVERS_STAGING_HOST\nFINAL_DECISION=BLOCKED_NEWFI_STAGING_EDGE_NO_EXISTING_TLS_CERT_COVERS_STAGING_HOST\nHOMELAB_RESULT_CONTRACT={\"changes_made\":false,\"command_id\":\"NEWFI-260908-A-STAGINGEDGE-APPLY1\",\"rollback_restored\":null,\"rollback_started\":false,\"status\":\"BLOCKED\",\"version\":1}\n" + "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" } diff --git a/runtime/latest.txt b/runtime/latest.txt index b7dea2a7..47c1a64f 100644 --- a/runtime/latest.txt +++ b/runtime/latest.txt @@ -1,22 +1,22 @@ CHAT_OUTPUT_BEGIN -COMMAND_ID=NEWFI-260908-A-STAGINGEDGE-APPLY1 -STATUS=BLOCKED -RC=2 +COMMAND_ID=SIGNALBOT-260908-RECONNECT-POLICY-RCA-037 +STATUS=OK +RC=0 HOST=pve01 -MODE=change -COMPONENT=newfi-staging-edge-route-tls-dns-apply +MODE=read-only +COMPONENT=signalbot-cr0116-reconnect-policy-rca REFERENCE_REGISTER_CHECK=OK REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 ERROR_REGISTER_CHECK=OK ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 -COMMAND_SHA256=1aae3a98b115a56ea222e88ead3d1b124de0dfe6a609794df2a677715b31e9bf +COMMAND_SHA256=692caa6f77d6852cd79ece2fe377b72066856f9f0c6b00e6fb1b177ab4e20643 DUPLICATE_FAILED_COMMAND_BLOCKED=false EXECUTION_STARTED=true -CHANGE_DECLARED=true +CHANGE_DECLARED=false RESULT_CONTRACT_VALID=true -RESULT_CONTRACT_STATUS=BLOCKED +RESULT_CONTRACT_STATUS=NOT_APPLICABLE RESULT_CONTRACT_ERROR=NONE -COMMAND_RC=2 +COMMAND_RC=0 CHANGES_MADE=false ROLLBACK_STARTED=false ROLLBACK_RESTORED=null @@ -24,29 +24,20 @@ MUTATION_OUTCOME=NO_MUTATION SANITIZED=yes SECRETS_INCLUDED=no PRIVATE_ADDRESSES_INCLUDED=no -RAW_EVIDENCE_SHA256=feb4cd657a3eec33fa1821da8fd4d9baf104ed2ce7dc34b1a3250b708fba5e27 -SANITIZED_OUTPUT_SHA256=42c3d7d4c61a6e31cd08d373384189a95bc3aa5d36da9e2a429071e1eee04823 +RAW_EVIDENCE_SHA256=61489801dbaae6b142aa5eca683f0d2f1cc3f35c69fbabf9a5d70b44f37f85c0 +SANITIZED_OUTPUT_SHA256=61489801dbaae6b142aa5eca683f0d2f1cc3f35c69fbabf9a5d70b44f37f85c0 OUTPUT_BEGIN -STAGINGEDGE_APPLY_BEGIN=true -COMMAND_ID=NEWFI-260908-A-STAGINGEDGE-APPLY1 -COMPONENT=newfi-staging-edge-route-tls-dns-apply -MODE=change ERROR_REGISTER_CHECK=OK -ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 REFERENCE_CHECK=OK -REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 AUTHORITY_CHECK_SCOPE=READABILITY_ONLY_INCIDENTS_NOT_CLOSED -RUNNER_SHA256=b248a4c32c9cc64e5747e7dce6c7fc0a23f5124a77c71ce72e27a81aceae9d2d -PRIOR_APPLY4_HISTORY_SHA256=329f346c5385910120b68b4596f8057319a09e822290e5f70da50fd4d469904f -PORTAL_PRODUCT_STATE_GATE=PASS -NEWFI_STAGING_IDENTITY={"name":"newfi-staging","node":"pve03","vmid":211} -STAGING_BACKEND_PREFLIGHT={"ip":"[PRIVATE_IP]","local_http":"503"} -EDGE_VM_IDENTITY={"name":"edge-vm","node":"pve03","vmid":130} -NPMPLUS_PREFLIGHT={"container":"npmplus","data_source":"/opt/npmplus","nginx":"/usr/local/nginx/sbin/nginx"} -EDGE_CF_TOKEN_GATE={"source_path":"/root/cert38-narrow-migration-20260904T074428Z/credentials-38.before","valid_count":1,"zone_id_sha256":"5a88f12ebe16612b4e42f11ee57b4b95ca4a13c4abb8f0009f09babfbec927e7"} -STAGINGEDGE_BLOCKED_REASON=NO_EXISTING_TLS_CERT_COVERS_STAGING_HOST -FINAL_DECISION=BLOCKED_NEWFI_STAGING_EDGE_NO_EXISTING_TLS_CERT_COVERS_STAGING_HOST -HOMELAB_RESULT_CONTRACT={"changes_made":false,"command_id":"NEWFI-260908-A-STAGINGEDGE-APPLY1","rollback_restored":null,"rollback_started":false,"status":"BLOCKED","version":1} +SB037_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"} +SB037_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}]} +SB037_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}} +SB037_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"} +SB037_DECISION=RECONNECT_POLICY_EVIDENCE_READY +SB037_MUTATION_SCOPE=NONE_READ_ONLY +SIGNALBOT037_DECISION=RECONNECT_POLICY_EVIDENCE_READY +SIGNALBOT037_MUTATION_SCOPE=NO_RUNTIME_NO_GIT_NO_EXCHANGE_MUTATION OUTPUT_END CHAT_OUTPUT_END