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
