diff --git a/runtime/history/SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1.json b/runtime/history/SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1.json new file mode 100644 index 00000000..514fd5f0 --- /dev/null +++ b/runtime/history/SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1.json @@ -0,0 +1,38 @@ +{ + "schema_version": 1, + "channel": "homelab-runtime", + "command_id": "SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1", + "status": "OK", + "rc": 0, + "host": "pve01", + "mode": "read-only", + "component": "signalbot-order-normalization-test-rca", + "started_at_utc": "2026-09-21T03:49:02Z", + "finished_at_utc": "2026-09-21T03:49:02Z", + "reference_register_checked": true, + "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", + "error_register_checked": true, + "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", + "command_sha256": "bb6b4fbaa3c7a735965adc6a9e217dc7242ed3097f157c63cb7d11fcba77a0fa", + "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": "6f146b4f6887dfacff94c79715d1e60e5cdb633c76d4b25a3f28feec05a11eab", + "sanitized_output_sha256": "6f146b4f6887dfacff94c79715d1e60e5cdb633c76d4b25a3f28feec05a11eab", + "output_truncated_in_json": false, + "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", + "output": "SIGNALBOT052FIXRCA1_RESULT={\"branch\":\"signalbot-260917-reconnect-reconciliation\",\"clean\":true,\"diff_empty\":true,\"head\":\"749073ae0f894debc06fb0ffbf7a84abf9da531f\",\"package_identity\":\"9dd3a15343a59007aa1c0484f9a52b5987c67478687f1a5d3e3c91c7b95a9073\",\"restored_exact\":true,\"source_lines_146_215\":\"146:\\n147: def fetch_trade_window(\\n148: self,\\n149: symbol: str,\\n150: *,\\n151: start_mts: datetime,\\n152: end_mts: datetime,\\n153: ) -> BitfinexTradeHistoryBatch:\\n154: if not isinstance(symbol, str) or not symbol.startswith(\\\"t\\\") or len(symbol) < 2:\\n155: raise DemonovError(\\\"Bitfinex trade history symbol invalid\\\")\\n156: start_mts = _utc(start_mts, \\\"start_mts\\\")\\n157: end_mts = _utc(end_mts, \\\"end_mts\\\")\\n158: if end_mts < start_mts:\\n159: raise DemonovError(\\\"Bitfinex trade history interval invalid\\\")\\n160: start_ms = int(start_mts.timestamp() * 1000)\\n161: end_ms = int(end_mts.timestamp() * 1000)\\n162: query = urlencode({\\\"start\\\": start_ms, \\\"end\\\": end_ms, \\\"sort\\\": 1, \\\"limit\\\": BITFINEX_PUBLIC_HISTORY_LIMIT})\\n163: url = f\\\"{BITFINEX_PUBLIC_TRADES_BASE}/{quote(symbol, safe='')}/hist?{query}\\\"\\n164: try:\\n165: payload = self._fetch_json(\\n166: url,\\n167: timeout=self.timeout,\\n168: max_bytes=self.max_bytes,\\n169: )\\n170: except DemonovError:\\n171: raise\\n172: except Exception as exc:\\n173: raise DemonovError(\\n174: \\\"Bitfinex public trade history request failed\\\"\\n175: ) from exc\\n176: if not isinstance(payload, list):\\n177: raise DemonovError(\\\"Bitfinex public trade history payload invalid\\\")\\n178:\\n179: normalized: list[BitfinexCanonicalTradeV1] = []\\n180: raw_rows: list[list[object]] = []\\n181: seen: dict[str, str] = {}\\n182: order_keys: list[tuple[datetime, int]] = []\\n183: for raw in payload:\\n184: if not isinstance(raw, list):\\n185: raise DemonovError(\\\"Bitfinex REST trade row malformed\\\")\\n186: trade = normalize_bitfinex_rest_trade(raw)\\n187: raw_rows.append(list(raw[:4]))\\n188: prior = seen.get(trade.trade_identity)\\n189: if prior is not None:\\n190: if prior != trade.semantic_hash:\\n191: raise DemonovError(\\\"Bitfinex REST trade semantic conflict\\\")\\n192: continue\\n193: seen[trade.trade_identity] = trade.semantic_hash\\n194: normalized.append(trade)\\n195: order_keys.append((trade.mts, trade.trade_id))\\n196: if order_keys != sorted(order_keys):\\n197: raise DemonovError(\\\"Bitfinex public trade history not ascending\\\")\\n198:\\n199: evidence_sha256 = canonical_hash(\\n200: {\\n201: \\\"schema\\\": \\\"CR0116_BITFINEX_RECONCILIATION_REST_EVIDENCE_V1\\\",\\n202: \\\"symbol\\\": symbol,\\n203: \\\"start_mts\\\": start_mts,\\n204: \\\"end_mts\\\": end_mts,\\n205: \\\"raw_row_count\\\": len(payload),\\n206: \\\"rows\\\": raw_rows,\\n207: }\\n208: )\\n209: return BitfinexTradeHistoryBatch(\\n210: symbol=symbol,\\n211: start_mts=start_mts,\\n212: end_mts=end_mts,\\n213: raw_row_count=len(payload),\\n214: trades=tuple(normalized),\\n215: evidence_sha256=evidence_sha256,\",\"source_sha256\":\"023ce0a041b50b866a48d9f752bd80479a34789d82f73c81d18246ed8871ef8e\",\"test_hits\":[\"126: journal.close()\\n127:\\n128:\\n129:def test_public_history_fetch_is_ascending_inclusive_and_deduplicates_exact_rows():\\n130: a, b, c = _trade(2001, 1), _trade(2002, 2), _trade(2003, 3)\\n131: calls = []\\n132: def fetch(url, *, timeout, max_bytes):\\n133: calls.append((url, timeout, max_bytes))\",\"133: calls.append((url, timeout, max_bytes))\\n134: return [_rest_row(a), _rest_row(b), _rest_row(b), _rest_row(c)]\\n135: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\\n136: batch = client.fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=c.mts)\\n137: assert [t.trade_id for t in batch.trades] == [2001, 2002, 2003]\\n138: assert batch.raw_row_count == 4\\n139: assert \\\"sort=1\\\" in calls[0][0] and \\\"limit=10000\\\" in calls[0][0]\\n140: assert f\\\"start={int(a.mts.timestamp()*1000)}\\\" in calls[0][0]\",\"145: a, b = _trade(2051, 1), _trade(2052, 2)\\n146: left = BitfinexPublicTradeHistoryClient(\\n147: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(a), _rest_row(b)]\\n148: ).fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n149: right = BitfinexPublicTradeHistoryClient(\\n150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\\n151: ).fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]\",\"148: ).fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n149: right = BitfinexPublicTradeHistoryClient(\\n150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\\n151: ).fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]\\n153: assert [t.trade_id for t in right.trades] == [2051, 2052]\\n154: assert left.raw_row_count == right.raw_row_count == 3\\n155: assert left.evidence_sha256 != right.evidence_sha256\",\"164: ]\\n165: for client in bad_clients:\\n166: with pytest.raises(DemonovError):\\n167: client.fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n168:\\n169:\\n170:def test_successful_reconciliation_recovers_middle_trade_and_is_idempotent(tmp_path):\\n171: journal, window, previous, next_trade = _journal_with_window(tmp_path)\",\"175: calls.append(url)\\n176: return [_rest_row(previous), _rest_row(middle), _rest_row(next_trade)]\\n177: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\\n178: resolved = client.reconcile_window(\\n179: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n180: )\\n181: repeated = client.reconcile_window(\\n182: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=21)\",\"178: resolved = client.reconcile_window(\\n179: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n180: )\\n181: repeated = client.reconcile_window(\\n182: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=21)\\n183: )\\n184: assert resolved.status is ReconciliationStatus.RECONCILED\\n185: assert resolved.recovered_row_count == 1\",\"189: journal.close()\\n190:\\n191:\\n192:def test_http_or_validation_failure_marks_window_unresolved(tmp_path):\\n193: journal, window, _, _ = _journal_with_window(tmp_path)\\n194: client = BitfinexPublicTradeHistoryClient(\\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\\\"simulated\\\"))\\n196: )\",\"194: client = BitfinexPublicTradeHistoryClient(\\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\\\"simulated\\\"))\\n196: )\\n197: resolved = client.reconcile_window(\\n198: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n199: )\\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n201: assert resolved.failure_reason == \\\"REST_FETCH_OR_VALIDATION_FAILED\\\"\",\"198: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n199: )\\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n201: assert resolved.failure_reason == \\\"REST_FETCH_OR_VALIDATION_FAILED\\\"\\n202: journal.close()\\n203:\\n204:\\n205:def test_exact_row_limit_marks_window_unresolved(tmp_path):\",\"212: rows[-1] = _rest_row(next_trade)\\n213: rows.sort(key=lambda r: (r[1], r[0]))\\n214: client = BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: rows)\\n215: resolved = client.reconcile_window(\\n216: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n217: )\\n218: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n219: assert resolved.failure_reason == \\\"REST_ROW_LIMIT_REACHED\\\"\",\"226: client = BitfinexPublicTradeHistoryClient(\\n227: fetch_json=lambda *a, **k: [_rest_row(previous), _rest_row(middle)]\\n228: )\\n229: resolved = client.reconcile_window(\\n230: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n231: )\\n232: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n233: assert resolved.failure_reason == \\\"NEXT_BOUNDARY_MISSING\\\"\",\"243: client = BitfinexPublicTradeHistoryClient(\\n244: fetch_json=lambda *a, **k: [conflicting, _rest_row(next_trade)]\\n245: )\\n246: resolved = client.reconcile_window(\\n247: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n248: )\\n249: after = journal._db.execute(\\n250: \\\"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\\\", (previous.trade_identity,)\"],\"test_lines_150_280\":\"150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\\n151: ).fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]\\n153: assert [t.trade_id for t in right.trades] == [2051, 2052]\\n154: assert left.raw_row_count == right.raw_row_count == 3\\n155: assert left.evidence_sha256 != right.evidence_sha256\\n156:\\n157:\\n158:def test_public_history_rejects_malformed_unordered_and_conflicting_duplicate_rows():\\n159: a, b = _trade(2101, 1), _trade(2102, 2)\\n160: bad_clients = [\\n161: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [[1, 2]]),\\n162: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [_rest_row(b), _rest_row(a)]),\\n163: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [_rest_row(a), [a.trade_id, int(a.mts.timestamp()*1000), \\\"0.5\\\", \\\"101\\\"]]),\\n164: ]\\n165: for client in bad_clients:\\n166: with pytest.raises(DemonovError):\\n167: client.fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n168:\\n169:\\n170:def test_successful_reconciliation_recovers_middle_trade_and_is_idempotent(tmp_path):\\n171: journal, window, previous, next_trade = _journal_with_window(tmp_path)\\n172: middle = _trade(1002, 7)\\n173: calls = []\\n174: def fetch(url, *, timeout, max_bytes):\\n175: calls.append(url)\\n176: return [_rest_row(previous), _rest_row(middle), _rest_row(next_trade)]\\n177: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\\n178: resolved = client.reconcile_window(\\n179: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n180: )\\n181: repeated = client.reconcile_window(\\n182: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=21)\\n183: )\\n184: assert resolved.status is ReconciliationStatus.RECONCILED\\n185: assert resolved.recovered_row_count == 1\\n186: assert journal.has_trade_identity(middle.trade_identity)\\n187: assert repeated == resolved\\n188: assert len(calls) == 1\\n189: journal.close()\\n190:\\n191:\\n192:def test_http_or_validation_failure_marks_window_unresolved(tmp_path):\\n193: journal, window, _, _ = _journal_with_window(tmp_path)\\n194: client = BitfinexPublicTradeHistoryClient(\\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\\\"simulated\\\"))\\n196: )\\n197: resolved = client.reconcile_window(\\n198: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n199: )\\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n201: assert resolved.failure_reason == \\\"REST_FETCH_OR_VALIDATION_FAILED\\\"\\n202: journal.close()\\n203:\\n204:\\n205:def test_exact_row_limit_marks_window_unresolved(tmp_path):\\n206: journal, window, previous, next_trade = _journal_with_window(tmp_path)\\n207: rows = []\\n208: for i in range(BITFINEX_PUBLIC_HISTORY_LIMIT):\\n209: trade_id = 300000 + i\\n210: rows.append([trade_id, int((previous.mts + timedelta(milliseconds=i)).timestamp()*1000), \\\"0.5\\\", \\\"100\\\"])\\n211: rows[0] = _rest_row(previous)\\n212: rows[-1] = _rest_row(next_trade)\\n213: rows.sort(key=lambda r: (r[1], r[0]))\\n214: client = BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: rows)\\n215: resolved = client.reconcile_window(\\n216: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n217: )\\n218: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n219: assert resolved.failure_reason == \\\"REST_ROW_LIMIT_REACHED\\\"\\n220: journal.close()\\n221:\\n222:\\n223:def test_missing_boundary_marks_window_unresolved(tmp_path):\\n224: journal, window, previous, _ = _journal_with_window(tmp_path, persist_next=False)\\n225: middle = _trade(1002, 7)\\n226: client = BitfinexPublicTradeHistoryClient(\\n227: fetch_json=lambda *a, **k: [_rest_row(previous), _rest_row(middle)]\\n228: )\\n229: resolved = client.reconcile_window(\\n230: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n231: )\\n232: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n233: assert resolved.failure_reason == \\\"NEXT_BOUNDARY_MISSING\\\"\\n234: journal.close()\\n235:\\n236:\\n237:def test_semantic_conflict_marks_window_unresolved_without_new_revision(tmp_path):\\n238: journal, window, previous, next_trade = _journal_with_window(tmp_path)\\n239: conflicting = [previous.trade_id, int(previous.mts.timestamp()*1000), \\\"0.5\\\", \\\"101\\\"]\\n240: before = journal._db.execute(\\n241: \\\"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\\\", (previous.trade_identity,)\\n242: ).fetchone()[\\\"n\\\"]\\n243: client = BitfinexPublicTradeHistoryClient(\\n244: fetch_json=lambda *a, **k: [conflicting, _rest_row(next_trade)]\\n245: )\\n246: resolved = client.reconcile_window(\\n247: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n248: )\\n249: after = journal._db.execute(\\n250: \\\"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\\\", (previous.trade_identity,)\\n251: ).fetchone()[\\\"n\\\"]\\n252: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n253: assert resolved.failure_reason == \\\"REST_SEMANTIC_CONFLICT\\\"\\n254: assert after == before\\n255: journal.close()\",\"test_sha256\":\"bebdd9c2875d2c63a0ac27d89eefa7f1c36c83c067aa8b472c0616ac470dd2a2\"}\nTASK_RESULT=PASS_SIGNALBOT_052FIXRCA1_ROLLBACK_AND_TEST_SEMANTICS_CAPTURED\nHOMELAB_RESULT_CONTRACT={\"version\":1,\"command_id\":\"SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1\",\"status\":\"OK\",\"changes_made\":false,\"rollback_started\":false,\"rollback_restored\":null}\n" +} diff --git a/runtime/history/SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1.txt b/runtime/history/SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1.txt new file mode 100644 index 00000000..c31d43a0 --- /dev/null +++ b/runtime/history/SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1.txt @@ -0,0 +1,35 @@ +CHAT_OUTPUT_BEGIN +COMMAND_ID=SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1 +STATUS=OK +RC=0 +HOST=pve01 +MODE=read-only +COMPONENT=signalbot-order-normalization-test-rca +REFERENCE_REGISTER_CHECK=OK +REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 +ERROR_REGISTER_CHECK=OK +ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 +COMMAND_SHA256=bb6b4fbaa3c7a735965adc6a9e217dc7242ed3097f157c63cb7d11fcba77a0fa +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=6f146b4f6887dfacff94c79715d1e60e5cdb633c76d4b25a3f28feec05a11eab +SANITIZED_OUTPUT_SHA256=6f146b4f6887dfacff94c79715d1e60e5cdb633c76d4b25a3f28feec05a11eab +OUTPUT_BEGIN +SIGNALBOT052FIXRCA1_RESULT={"branch":"signalbot-260917-reconnect-reconciliation","clean":true,"diff_empty":true,"head":"749073ae0f894debc06fb0ffbf7a84abf9da531f","package_identity":"9dd3a15343a59007aa1c0484f9a52b5987c67478687f1a5d3e3c91c7b95a9073","restored_exact":true,"source_lines_146_215":"146:\n147: def fetch_trade_window(\n148: self,\n149: symbol: str,\n150: *,\n151: start_mts: datetime,\n152: end_mts: datetime,\n153: ) -> BitfinexTradeHistoryBatch:\n154: if not isinstance(symbol, str) or not symbol.startswith(\"t\") or len(symbol) < 2:\n155: raise DemonovError(\"Bitfinex trade history symbol invalid\")\n156: start_mts = _utc(start_mts, \"start_mts\")\n157: end_mts = _utc(end_mts, \"end_mts\")\n158: if end_mts < start_mts:\n159: raise DemonovError(\"Bitfinex trade history interval invalid\")\n160: start_ms = int(start_mts.timestamp() * 1000)\n161: end_ms = int(end_mts.timestamp() * 1000)\n162: query = urlencode({\"start\": start_ms, \"end\": end_ms, \"sort\": 1, \"limit\": BITFINEX_PUBLIC_HISTORY_LIMIT})\n163: url = f\"{BITFINEX_PUBLIC_TRADES_BASE}/{quote(symbol, safe='')}/hist?{query}\"\n164: try:\n165: payload = self._fetch_json(\n166: url,\n167: timeout=self.timeout,\n168: max_bytes=self.max_bytes,\n169: )\n170: except DemonovError:\n171: raise\n172: except Exception as exc:\n173: raise DemonovError(\n174: \"Bitfinex public trade history request failed\"\n175: ) from exc\n176: if not isinstance(payload, list):\n177: raise DemonovError(\"Bitfinex public trade history payload invalid\")\n178:\n179: normalized: list[BitfinexCanonicalTradeV1] = []\n180: raw_rows: list[list[object]] = []\n181: seen: dict[str, str] = {}\n182: order_keys: list[tuple[datetime, int]] = []\n183: for raw in payload:\n184: if not isinstance(raw, list):\n185: raise DemonovError(\"Bitfinex REST trade row malformed\")\n186: trade = normalize_bitfinex_rest_trade(raw)\n187: raw_rows.append(list(raw[:4]))\n188: prior = seen.get(trade.trade_identity)\n189: if prior is not None:\n190: if prior != trade.semantic_hash:\n191: raise DemonovError(\"Bitfinex REST trade semantic conflict\")\n192: continue\n193: seen[trade.trade_identity] = trade.semantic_hash\n194: normalized.append(trade)\n195: order_keys.append((trade.mts, trade.trade_id))\n196: if order_keys != sorted(order_keys):\n197: raise DemonovError(\"Bitfinex public trade history not ascending\")\n198:\n199: evidence_sha256 = canonical_hash(\n200: {\n201: \"schema\": \"CR0116_BITFINEX_RECONCILIATION_REST_EVIDENCE_V1\",\n202: \"symbol\": symbol,\n203: \"start_mts\": start_mts,\n204: \"end_mts\": end_mts,\n205: \"raw_row_count\": len(payload),\n206: \"rows\": raw_rows,\n207: }\n208: )\n209: return BitfinexTradeHistoryBatch(\n210: symbol=symbol,\n211: start_mts=start_mts,\n212: end_mts=end_mts,\n213: raw_row_count=len(payload),\n214: trades=tuple(normalized),\n215: evidence_sha256=evidence_sha256,","source_sha256":"023ce0a041b50b866a48d9f752bd80479a34789d82f73c81d18246ed8871ef8e","test_hits":["126: journal.close()\n127:\n128:\n129:def test_public_history_fetch_is_ascending_inclusive_and_deduplicates_exact_rows():\n130: a, b, c = _trade(2001, 1), _trade(2002, 2), _trade(2003, 3)\n131: calls = []\n132: def fetch(url, *, timeout, max_bytes):\n133: calls.append((url, timeout, max_bytes))","133: calls.append((url, timeout, max_bytes))\n134: return [_rest_row(a), _rest_row(b), _rest_row(b), _rest_row(c)]\n135: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\n136: batch = client.fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=c.mts)\n137: assert [t.trade_id for t in batch.trades] == [2001, 2002, 2003]\n138: assert batch.raw_row_count == 4\n139: assert \"sort=1\" in calls[0][0] and \"limit=10000\" in calls[0][0]\n140: assert f\"start={int(a.mts.timestamp()*1000)}\" in calls[0][0]","145: a, b = _trade(2051, 1), _trade(2052, 2)\n146: left = BitfinexPublicTradeHistoryClient(\n147: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(a), _rest_row(b)]\n148: ).fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n149: right = BitfinexPublicTradeHistoryClient(\n150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\n151: ).fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]","148: ).fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n149: right = BitfinexPublicTradeHistoryClient(\n150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\n151: ).fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]\n153: assert [t.trade_id for t in right.trades] == [2051, 2052]\n154: assert left.raw_row_count == right.raw_row_count == 3\n155: assert left.evidence_sha256 != right.evidence_sha256","164: ]\n165: for client in bad_clients:\n166: with pytest.raises(DemonovError):\n167: client.fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n168:\n169:\n170:def test_successful_reconciliation_recovers_middle_trade_and_is_idempotent(tmp_path):\n171: journal, window, previous, next_trade = _journal_with_window(tmp_path)","175: calls.append(url)\n176: return [_rest_row(previous), _rest_row(middle), _rest_row(next_trade)]\n177: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\n178: resolved = client.reconcile_window(\n179: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n180: )\n181: repeated = client.reconcile_window(\n182: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=21)","178: resolved = client.reconcile_window(\n179: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n180: )\n181: repeated = client.reconcile_window(\n182: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=21)\n183: )\n184: assert resolved.status is ReconciliationStatus.RECONCILED\n185: assert resolved.recovered_row_count == 1","189: journal.close()\n190:\n191:\n192:def test_http_or_validation_failure_marks_window_unresolved(tmp_path):\n193: journal, window, _, _ = _journal_with_window(tmp_path)\n194: client = BitfinexPublicTradeHistoryClient(\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\"simulated\"))\n196: )","194: client = BitfinexPublicTradeHistoryClient(\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\"simulated\"))\n196: )\n197: resolved = client.reconcile_window(\n198: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n199: )\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\n201: assert resolved.failure_reason == \"REST_FETCH_OR_VALIDATION_FAILED\"","198: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n199: )\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\n201: assert resolved.failure_reason == \"REST_FETCH_OR_VALIDATION_FAILED\"\n202: journal.close()\n203:\n204:\n205:def test_exact_row_limit_marks_window_unresolved(tmp_path):","212: rows[-1] = _rest_row(next_trade)\n213: rows.sort(key=lambda r: (r[1], r[0]))\n214: client = BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: rows)\n215: resolved = client.reconcile_window(\n216: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n217: )\n218: assert resolved.status is ReconciliationStatus.UNRESOLVED\n219: assert resolved.failure_reason == \"REST_ROW_LIMIT_REACHED\"","226: client = BitfinexPublicTradeHistoryClient(\n227: fetch_json=lambda *a, **k: [_rest_row(previous), _rest_row(middle)]\n228: )\n229: resolved = client.reconcile_window(\n230: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n231: )\n232: assert resolved.status is ReconciliationStatus.UNRESOLVED\n233: assert resolved.failure_reason == \"NEXT_BOUNDARY_MISSING\"","243: client = BitfinexPublicTradeHistoryClient(\n244: fetch_json=lambda *a, **k: [conflicting, _rest_row(next_trade)]\n245: )\n246: resolved = client.reconcile_window(\n247: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n248: )\n249: after = journal._db.execute(\n250: \"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\", (previous.trade_identity,)"],"test_lines_150_280":"150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\n151: ).fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]\n153: assert [t.trade_id for t in right.trades] == [2051, 2052]\n154: assert left.raw_row_count == right.raw_row_count == 3\n155: assert left.evidence_sha256 != right.evidence_sha256\n156:\n157:\n158:def test_public_history_rejects_malformed_unordered_and_conflicting_duplicate_rows():\n159: a, b = _trade(2101, 1), _trade(2102, 2)\n160: bad_clients = [\n161: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [[1, 2]]),\n162: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [_rest_row(b), _rest_row(a)]),\n163: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [_rest_row(a), [a.trade_id, int(a.mts.timestamp()*1000), \"0.5\", \"101\"]]),\n164: ]\n165: for client in bad_clients:\n166: with pytest.raises(DemonovError):\n167: client.fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n168:\n169:\n170:def test_successful_reconciliation_recovers_middle_trade_and_is_idempotent(tmp_path):\n171: journal, window, previous, next_trade = _journal_with_window(tmp_path)\n172: middle = _trade(1002, 7)\n173: calls = []\n174: def fetch(url, *, timeout, max_bytes):\n175: calls.append(url)\n176: return [_rest_row(previous), _rest_row(middle), _rest_row(next_trade)]\n177: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\n178: resolved = client.reconcile_window(\n179: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n180: )\n181: repeated = client.reconcile_window(\n182: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=21)\n183: )\n184: assert resolved.status is ReconciliationStatus.RECONCILED\n185: assert resolved.recovered_row_count == 1\n186: assert journal.has_trade_identity(middle.trade_identity)\n187: assert repeated == resolved\n188: assert len(calls) == 1\n189: journal.close()\n190:\n191:\n192:def test_http_or_validation_failure_marks_window_unresolved(tmp_path):\n193: journal, window, _, _ = _journal_with_window(tmp_path)\n194: client = BitfinexPublicTradeHistoryClient(\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\"simulated\"))\n196: )\n197: resolved = client.reconcile_window(\n198: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n199: )\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\n201: assert resolved.failure_reason == \"REST_FETCH_OR_VALIDATION_FAILED\"\n202: journal.close()\n203:\n204:\n205:def test_exact_row_limit_marks_window_unresolved(tmp_path):\n206: journal, window, previous, next_trade = _journal_with_window(tmp_path)\n207: rows = []\n208: for i in range(BITFINEX_PUBLIC_HISTORY_LIMIT):\n209: trade_id = 300000 + i\n210: rows.append([trade_id, int((previous.mts + timedelta(milliseconds=i)).timestamp()*1000), \"0.5\", \"100\"])\n211: rows[0] = _rest_row(previous)\n212: rows[-1] = _rest_row(next_trade)\n213: rows.sort(key=lambda r: (r[1], r[0]))\n214: client = BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: rows)\n215: resolved = client.reconcile_window(\n216: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n217: )\n218: assert resolved.status is ReconciliationStatus.UNRESOLVED\n219: assert resolved.failure_reason == \"REST_ROW_LIMIT_REACHED\"\n220: journal.close()\n221:\n222:\n223:def test_missing_boundary_marks_window_unresolved(tmp_path):\n224: journal, window, previous, _ = _journal_with_window(tmp_path, persist_next=False)\n225: middle = _trade(1002, 7)\n226: client = BitfinexPublicTradeHistoryClient(\n227: fetch_json=lambda *a, **k: [_rest_row(previous), _rest_row(middle)]\n228: )\n229: resolved = client.reconcile_window(\n230: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n231: )\n232: assert resolved.status is ReconciliationStatus.UNRESOLVED\n233: assert resolved.failure_reason == \"NEXT_BOUNDARY_MISSING\"\n234: journal.close()\n235:\n236:\n237:def test_semantic_conflict_marks_window_unresolved_without_new_revision(tmp_path):\n238: journal, window, previous, next_trade = _journal_with_window(tmp_path)\n239: conflicting = [previous.trade_id, int(previous.mts.timestamp()*1000), \"0.5\", \"101\"]\n240: before = journal._db.execute(\n241: \"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\", (previous.trade_identity,)\n242: ).fetchone()[\"n\"]\n243: client = BitfinexPublicTradeHistoryClient(\n244: fetch_json=lambda *a, **k: [conflicting, _rest_row(next_trade)]\n245: )\n246: resolved = client.reconcile_window(\n247: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n248: )\n249: after = journal._db.execute(\n250: \"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\", (previous.trade_identity,)\n251: ).fetchone()[\"n\"]\n252: assert resolved.status is ReconciliationStatus.UNRESOLVED\n253: assert resolved.failure_reason == \"REST_SEMANTIC_CONFLICT\"\n254: assert after == before\n255: journal.close()","test_sha256":"bebdd9c2875d2c63a0ac27d89eefa7f1c36c83c067aa8b472c0616ac470dd2a2"} +TASK_RESULT=PASS_SIGNALBOT_052FIXRCA1_ROLLBACK_AND_TEST_SEMANTICS_CAPTURED +HOMELAB_RESULT_CONTRACT={"version":1,"command_id":"SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1","status":"OK","changes_made":false,"rollback_started":false,"rollback_restored":null} + +OUTPUT_END +CHAT_OUTPUT_END diff --git a/runtime/latest.json b/runtime/latest.json index 30e5176a..514fd5f0 100644 --- a/runtime/latest.json +++ b/runtime/latest.json @@ -1,38 +1,38 @@ { "schema_version": 1, "channel": "homelab-runtime", - "command_id": "NEWFI-260921-NEWFDOM-MEDICAL218-FINAL347", + "command_id": "SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1", "status": "OK", "rc": 0, "host": "pve01", - "mode": "change", - "component": "newfi-medical218-private-owner-records-staging-production-git-final", - "started_at_utc": "2026-09-21T03:47:52Z", - "finished_at_utc": "2026-09-21T03:48:17Z", + "mode": "read-only", + "component": "signalbot-order-normalization-test-rca", + "started_at_utc": "2026-09-21T03:49:02Z", + "finished_at_utc": "2026-09-21T03:49:02Z", "reference_register_checked": true, "reference_sha256": "5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66", "error_register_checked": true, "error_register_sha256": "3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0", - "command_sha256": "7c0be8f606fd9d478f81be16fc1479f945886af6c6557c9d912542fa88619385", + "command_sha256": "bb6b4fbaa3c7a735965adc6a9e217dc7242ed3097f157c63cb7d11fcba77a0fa", "duplicate_failed_command_blocked": false, "block_reason": null, "execution_started": true, - "change_declared": true, + "change_declared": false, "result_contract_valid": true, - "result_contract_status": "OK", + "result_contract_status": null, "result_contract_error": null, "command_rc": 0, - "changes_made": true, + "changes_made": false, "rollback_started": false, "rollback_restored": null, - "mutation_outcome": "MUTATED", + "mutation_outcome": "NO_MUTATION", "sanitized": true, "secrets_included": false, "private_addresses_included": false, "raw_evidence_retained_locally": true, - "raw_evidence_sha256": "d43a0dcda6732018020a05563f8cb16c17444852019be703abb05e596ec0c711", - "sanitized_output_sha256": "d43a0dcda6732018020a05563f8cb16c17444852019be703abb05e596ec0c711", + "raw_evidence_sha256": "6f146b4f6887dfacff94c79715d1e60e5cdb633c76d4b25a3f28feec05a11eab", + "sanitized_output_sha256": "6f146b4f6887dfacff94c79715d1e60e5cdb633c76d4b25a3f28feec05a11eab", "output_truncated_in_json": false, "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", - "output": "MED347_BEGIN=true\nCOMMAND_ID=NEWFI-260921-NEWFDOM-MEDICAL218-FINAL347\nIMMUTABLE_PLAN_CHECK=PASS\nGIT_PRESTATE={\"branch\":\"implementation/newfi-closed-mvp-20260906\",\"clean\":true,\"head\":\"33b6f020b8c7bd6adc57f51c164ff028aadc62cc\",\"remote\":\"33b6f020b8c7bd6adc57f51c164ff028aadc62cc\"}\nCANDIDATE={\"documents\":\"PRIVATE_HTTPS_REFERENCE\",\"files\":{\"Pub/Controller/Medical.php\":\"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f\",\"Pub/View/Medical.php\":\"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d\",\"Service/Medical.php\":\"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283\",\"Setup.php\":\"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a\",\"_data/routes.xml\":\"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6\",\"_data/templates.xml\":\"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2\",\"addon.json\":\"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384\"},\"fixed_bundle\":\"cb6bcf8c661bd0a8ee1cda229a67e5983f4f0925d5481fc12e554d9bef529711\",\"old_dogs_sha\":\"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5\",\"patched_dogs_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"privacy\":\"OWNER_OR_ADMIN_ONLY\",\"reminders\":\"RECORD_DUE_STATE_NO_ALERT_SERVICE\"}\nPUBLIC_PRE={\"/\":\"200\",\"/dogs/\":\"200\",\"/events/\":\"200\",\"/map/\":\"200\",\"/medical/\":\"404\",\"/pedigree/\":\"200\"}\nVM211_PREFLIGHT={\"action\":\"preflight\",\"result\":{\"addon\":null,\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":false,\"dogs_rows\":0,\"dogs_tpl_sha\":\"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5\",\"errors_count\":29,\"errors_fp\":\"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db\",\"errors_max\":61,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":0,\"rows\":0,\"source_exists\":false,\"source_manifest\":{},\"table\":0,\"templates\":0},\"schema_version\":1,\"status\":\"PASS\"}\nVM210_PREFLIGHT={\"action\":\"preflight\",\"result\":{\"addon\":null,\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":false,\"dogs_rows\":0,\"dogs_tpl_sha\":\"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5\",\"errors_count\":46,\"errors_fp\":\"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d\",\"errors_max\":46,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":0,\"rows\":0,\"source_exists\":false,\"source_manifest\":{},\"table\":0,\"templates\":0},\"schema_version\":1,\"status\":\"PASS\"}\nVM211_APPLY={\"action\":\"apply\",\"result\":{\"fixed_bundle\":\"cb6bcf8c661bd0a8ee1cda229a67e5983f4f0925d5481fc12e554d9bef529711\",\"meta\":{\"old_dogs_sha\":\"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5\",\"patched_dogs_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"pre\":{\"addon\":null,\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":false,\"dogs_rows\":0,\"dogs_tpl_sha\":\"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5\",\"errors_count\":29,\"errors_fp\":\"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db\",\"errors_max\":61,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":0,\"rows\":0,\"source_exists\":false,\"source_manifest\":{},\"table\":0,\"templates\":0}},\"state\":{\"addon\":{\"active\":1,\"addon_id\":\"Newfi/Medical\",\"is_processing\":0,\"version_id\":1000010},\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":true,\"dogs_rows\":0,\"dogs_tpl_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"errors_count\":29,\"errors_fp\":\"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db\",\"errors_max\":61,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":1,\"rows\":0,\"source_exists\":true,\"source_manifest\":{\"Pub/Controller/Medical.php\":\"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f\",\"Pub/View/Medical.php\":\"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d\",\"Service/Medical.php\":\"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283\",\"Setup.php\":\"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a\",\"_data/routes.xml\":\"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6\",\"_data/templates.xml\":\"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2\",\"addon.json\":\"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384\"},\"table\":1,\"templates\":3}},\"schema_version\":1,\"status\":\"PASS\"}\nVM211_SYNTHETIC={\"action\":\"synthetic\",\"result\":{\"state\":{\"addon\":{\"active\":1,\"addon_id\":\"Newfi/Medical\",\"is_processing\":0,\"version_id\":1000010},\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":true,\"dogs_rows\":0,\"dogs_tpl_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"errors_count\":29,\"errors_fp\":\"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db\",\"errors_max\":61,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":1,\"rows\":0,\"source_exists\":true,\"source_manifest\":{\"Pub/Controller/Medical.php\":\"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f\",\"Pub/View/Medical.php\":\"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d\",\"Service/Medical.php\":\"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283\",\"Setup.php\":\"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a\",\"_data/routes.xml\":\"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6\",\"_data/templates.xml\":\"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2\",\"addon.json\":\"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384\"},\"table\":1,\"templates\":3},\"synthetic\":{\"delete\":true,\"dog_rows_after\":0,\"guest_denied\":true,\"https_document_validation\":true,\"medical_rows_after\":0,\"owner_policy\":true,\"private_template\":true,\"record_types\":true,\"records_during\":5,\"reminder_state\":true,\"weight_history\":true}},\"schema_version\":1,\"status\":\"PASS\"}\nVM211_RESTORE={\"action\":\"restore\",\"result\":{\"addon\":null,\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":false,\"dogs_rows\":0,\"dogs_tpl_sha\":\"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5\",\"errors_count\":29,\"errors_fp\":\"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db\",\"errors_max\":61,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":0,\"rows\":0,\"source_exists\":false,\"source_manifest\":{},\"table\":0,\"templates\":0},\"schema_version\":1,\"status\":\"PASS\"}\nVM211_APPLY={\"action\":\"apply\",\"result\":{\"fixed_bundle\":\"cb6bcf8c661bd0a8ee1cda229a67e5983f4f0925d5481fc12e554d9bef529711\",\"meta\":{\"old_dogs_sha\":\"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5\",\"patched_dogs_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"pre\":{\"addon\":null,\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":false,\"dogs_rows\":0,\"dogs_tpl_sha\":\"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5\",\"errors_count\":29,\"errors_fp\":\"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db\",\"errors_max\":61,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":0,\"rows\":0,\"source_exists\":false,\"source_manifest\":{},\"table\":0,\"templates\":0}},\"state\":{\"addon\":{\"active\":1,\"addon_id\":\"Newfi/Medical\",\"is_processing\":0,\"version_id\":1000010},\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":true,\"dogs_rows\":0,\"dogs_tpl_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"errors_count\":29,\"errors_fp\":\"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db\",\"errors_max\":61,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":1,\"rows\":0,\"source_exists\":true,\"source_manifest\":{\"Pub/Controller/Medical.php\":\"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f\",\"Pub/View/Medical.php\":\"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d\",\"Service/Medical.php\":\"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283\",\"Setup.php\":\"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a\",\"_data/routes.xml\":\"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6\",\"_data/templates.xml\":\"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2\",\"addon.json\":\"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384\"},\"table\":1,\"templates\":3}},\"schema_version\":1,\"status\":\"PASS\"}\nVM211_SYNTHETIC={\"action\":\"synthetic\",\"result\":{\"state\":{\"addon\":{\"active\":1,\"addon_id\":\"Newfi/Medical\",\"is_processing\":0,\"version_id\":1000010},\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":true,\"dogs_rows\":0,\"dogs_tpl_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"errors_count\":29,\"errors_fp\":\"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db\",\"errors_max\":61,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":1,\"rows\":0,\"source_exists\":true,\"source_manifest\":{\"Pub/Controller/Medical.php\":\"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f\",\"Pub/View/Medical.php\":\"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d\",\"Service/Medical.php\":\"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283\",\"Setup.php\":\"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a\",\"_data/routes.xml\":\"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6\",\"_data/templates.xml\":\"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2\",\"addon.json\":\"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384\"},\"table\":1,\"templates\":3},\"synthetic\":{\"delete\":true,\"dog_rows_after\":0,\"guest_denied\":true,\"https_document_validation\":true,\"medical_rows_after\":0,\"owner_policy\":true,\"private_template\":true,\"record_types\":true,\"records_during\":5,\"reminder_state\":true,\"weight_history\":true}},\"schema_version\":1,\"status\":\"PASS\"}\nVM211_AUDIT={\"action\":\"audit\",\"result\":{\"addon\":{\"active\":1,\"addon_id\":\"Newfi/Medical\",\"is_processing\":0,\"version_id\":1000010},\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":true,\"dogs_rows\":0,\"dogs_tpl_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"errors_count\":29,\"errors_fp\":\"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db\",\"errors_max\":61,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":1,\"rows\":0,\"source_exists\":true,\"source_manifest\":{\"Pub/Controller/Medical.php\":\"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f\",\"Pub/View/Medical.php\":\"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d\",\"Service/Medical.php\":\"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283\",\"Setup.php\":\"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a\",\"_data/routes.xml\":\"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6\",\"_data/templates.xml\":\"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2\",\"addon.json\":\"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384\"},\"table\":1,\"templates\":3},\"schema_version\":1,\"status\":\"PASS\"}\nVM210_APPLY={\"action\":\"apply\",\"result\":{\"fixed_bundle\":\"cb6bcf8c661bd0a8ee1cda229a67e5983f4f0925d5481fc12e554d9bef529711\",\"meta\":{\"old_dogs_sha\":\"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5\",\"patched_dogs_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"pre\":{\"addon\":null,\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":false,\"dogs_rows\":0,\"dogs_tpl_sha\":\"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5\",\"errors_count\":46,\"errors_fp\":\"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d\",\"errors_max\":46,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":0,\"rows\":0,\"source_exists\":false,\"source_manifest\":{},\"table\":0,\"templates\":0}},\"state\":{\"addon\":{\"active\":1,\"addon_id\":\"Newfi/Medical\",\"is_processing\":0,\"version_id\":1000010},\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":true,\"dogs_rows\":0,\"dogs_tpl_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"errors_count\":46,\"errors_fp\":\"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d\",\"errors_max\":46,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":1,\"rows\":0,\"source_exists\":true,\"source_manifest\":{\"Pub/Controller/Medical.php\":\"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f\",\"Pub/View/Medical.php\":\"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d\",\"Service/Medical.php\":\"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283\",\"Setup.php\":\"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a\",\"_data/routes.xml\":\"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6\",\"_data/templates.xml\":\"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2\",\"addon.json\":\"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384\"},\"table\":1,\"templates\":3}},\"schema_version\":1,\"status\":\"PASS\"}\nVM210_AUDIT={\"action\":\"audit\",\"result\":{\"addon\":{\"active\":1,\"addon_id\":\"Newfi/Medical\",\"is_processing\":0,\"version_id\":1000010},\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":true,\"dogs_rows\":0,\"dogs_tpl_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"errors_count\":46,\"errors_fp\":\"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d\",\"errors_max\":46,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":1,\"rows\":0,\"source_exists\":true,\"source_manifest\":{\"Pub/Controller/Medical.php\":\"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f\",\"Pub/View/Medical.php\":\"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d\",\"Service/Medical.php\":\"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283\",\"Setup.php\":\"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a\",\"_data/routes.xml\":\"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6\",\"_data/templates.xml\":\"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2\",\"addon.json\":\"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384\"},\"table\":1,\"templates\":3},\"schema_version\":1,\"status\":\"PASS\"}\nPUBLIC_PRIVACY={\"base\":{\"/\":\"200\",\"/dogs/\":\"200\",\"/events/\":\"200\",\"/map/\":\"200\",\"/pedigree/\":\"200\"},\"fake_bytes\":26312,\"fake_http\":\"404\",\"fake_stderr_sha\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\",\"medical_bytes\":30366,\"medical_http\":\"403\",\"medical_stderr_sha\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\",\"private_marker_fake\":false,\"private_marker_medical\":false}\nVM210_AUDIT={\"action\":\"audit\",\"result\":{\"addon\":{\"active\":1,\"addon_id\":\"Newfi/Medical\",\"is_processing\":0,\"version_id\":1000010},\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":true,\"dogs_rows\":0,\"dogs_tpl_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"errors_count\":46,\"errors_fp\":\"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d\",\"errors_max\":46,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":1,\"rows\":0,\"source_exists\":true,\"source_manifest\":{\"Pub/Controller/Medical.php\":\"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f\",\"Pub/View/Medical.php\":\"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d\",\"Service/Medical.php\":\"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283\",\"Setup.php\":\"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a\",\"_data/routes.xml\":\"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6\",\"_data/templates.xml\":\"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2\",\"addon.json\":\"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384\"},\"table\":1,\"templates\":3},\"schema_version\":1,\"status\":\"PASS\"}\nGIT_CANDIDATE_HEAD=8ee9246024eabf84efbf21dc9b02ae2a666dca26\nGIT_PUSH_RESULT={\"rc\":0,\"remote_head\":\"8ee9246024eabf84efbf21dc9b02ae2a666dca26\",\"stderr_sha\":\"1943a695309bf7e7dcc9ef182d4e1178117cd6602f683618a6e4df58a754ffd3\",\"stdout_sha\":\"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"}\nGIT_FINAL={\"branch\":\"implementation/newfi-closed-mvp-20260906\",\"clean\":true,\"head\":\"8ee9246024eabf84efbf21dc9b02ae2a666dca26\",\"remote\":\"8ee9246024eabf84efbf21dc9b02ae2a666dca26\"}\nVM211_AUDIT={\"action\":\"audit\",\"result\":{\"addon\":{\"active\":1,\"addon_id\":\"Newfi/Medical\",\"is_processing\":0,\"version_id\":1000010},\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":true,\"dogs_rows\":0,\"dogs_tpl_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"errors_count\":29,\"errors_fp\":\"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db\",\"errors_max\":61,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":1,\"rows\":0,\"source_exists\":true,\"source_manifest\":{\"Pub/Controller/Medical.php\":\"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f\",\"Pub/View/Medical.php\":\"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d\",\"Service/Medical.php\":\"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283\",\"Setup.php\":\"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a\",\"_data/routes.xml\":\"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6\",\"_data/templates.xml\":\"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2\",\"addon.json\":\"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384\"},\"table\":1,\"templates\":3},\"schema_version\":1,\"status\":\"PASS\"}\nVM210_AUDIT={\"action\":\"audit\",\"result\":{\"addon\":{\"active\":1,\"addon_id\":\"Newfi/Medical\",\"is_processing\":0,\"version_id\":1000010},\"dog_policy_class\":true,\"dogs_addon\":{\"active\":1,\"addon_id\":\"Newfi/Dogs\",\"is_processing\":0,\"version_id\":1000010},\"dogs_medical_link_source\":true,\"dogs_rows\":0,\"dogs_tpl_sha\":\"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876\",\"errors_count\":46,\"errors_fp\":\"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d\",\"errors_max\":46,\"pedigree_addon\":{\"active\":1,\"addon_id\":\"Newfi/Pedigree\",\"is_processing\":0,\"version_id\":1000010},\"pedigree_rows\":0,\"route\":1,\"rows\":0,\"source_exists\":true,\"source_manifest\":{\"Pub/Controller/Medical.php\":\"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f\",\"Pub/View/Medical.php\":\"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d\",\"Service/Medical.php\":\"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283\",\"Setup.php\":\"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a\",\"_data/routes.xml\":\"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6\",\"_data/templates.xml\":\"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2\",\"addon.json\":\"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384\"},\"table\":1,\"templates\":3},\"schema_version\":1,\"status\":\"PASS\"}\nPUBLIC_FINAL={\"base\":{\"/\":\"200\",\"/dogs/\":\"200\",\"/events/\":\"200\",\"/map/\":\"200\",\"/pedigree/\":\"200\"},\"medical_http\":\"403\",\"private_marker\":false}\nMEDICAL218_CLOSED=True\nDECISION=PASS_MEDICAL218_PRIVATE_RECORDS_PRODUCTION_GIT_FINAL\nNEXT_GATE=KENNELS219\nACTIVE_MAJOR_STAGE=KENNELS219\nMAJOR_STAGES_REMAINING=3\nGIT_NEW_HEAD=8ee9246024eabf84efbf21dc9b02ae2a666dca26\nHOMELAB_RESULT_CONTRACT={\"changes_made\":true,\"command_id\":\"NEWFI-260921-NEWFDOM-MEDICAL218-FINAL347\",\"rollback_restored\":null,\"rollback_started\":false,\"status\":\"OK\",\"version\":1}\nMED347_END=true\n" + "output": "SIGNALBOT052FIXRCA1_RESULT={\"branch\":\"signalbot-260917-reconnect-reconciliation\",\"clean\":true,\"diff_empty\":true,\"head\":\"749073ae0f894debc06fb0ffbf7a84abf9da531f\",\"package_identity\":\"9dd3a15343a59007aa1c0484f9a52b5987c67478687f1a5d3e3c91c7b95a9073\",\"restored_exact\":true,\"source_lines_146_215\":\"146:\\n147: def fetch_trade_window(\\n148: self,\\n149: symbol: str,\\n150: *,\\n151: start_mts: datetime,\\n152: end_mts: datetime,\\n153: ) -> BitfinexTradeHistoryBatch:\\n154: if not isinstance(symbol, str) or not symbol.startswith(\\\"t\\\") or len(symbol) < 2:\\n155: raise DemonovError(\\\"Bitfinex trade history symbol invalid\\\")\\n156: start_mts = _utc(start_mts, \\\"start_mts\\\")\\n157: end_mts = _utc(end_mts, \\\"end_mts\\\")\\n158: if end_mts < start_mts:\\n159: raise DemonovError(\\\"Bitfinex trade history interval invalid\\\")\\n160: start_ms = int(start_mts.timestamp() * 1000)\\n161: end_ms = int(end_mts.timestamp() * 1000)\\n162: query = urlencode({\\\"start\\\": start_ms, \\\"end\\\": end_ms, \\\"sort\\\": 1, \\\"limit\\\": BITFINEX_PUBLIC_HISTORY_LIMIT})\\n163: url = f\\\"{BITFINEX_PUBLIC_TRADES_BASE}/{quote(symbol, safe='')}/hist?{query}\\\"\\n164: try:\\n165: payload = self._fetch_json(\\n166: url,\\n167: timeout=self.timeout,\\n168: max_bytes=self.max_bytes,\\n169: )\\n170: except DemonovError:\\n171: raise\\n172: except Exception as exc:\\n173: raise DemonovError(\\n174: \\\"Bitfinex public trade history request failed\\\"\\n175: ) from exc\\n176: if not isinstance(payload, list):\\n177: raise DemonovError(\\\"Bitfinex public trade history payload invalid\\\")\\n178:\\n179: normalized: list[BitfinexCanonicalTradeV1] = []\\n180: raw_rows: list[list[object]] = []\\n181: seen: dict[str, str] = {}\\n182: order_keys: list[tuple[datetime, int]] = []\\n183: for raw in payload:\\n184: if not isinstance(raw, list):\\n185: raise DemonovError(\\\"Bitfinex REST trade row malformed\\\")\\n186: trade = normalize_bitfinex_rest_trade(raw)\\n187: raw_rows.append(list(raw[:4]))\\n188: prior = seen.get(trade.trade_identity)\\n189: if prior is not None:\\n190: if prior != trade.semantic_hash:\\n191: raise DemonovError(\\\"Bitfinex REST trade semantic conflict\\\")\\n192: continue\\n193: seen[trade.trade_identity] = trade.semantic_hash\\n194: normalized.append(trade)\\n195: order_keys.append((trade.mts, trade.trade_id))\\n196: if order_keys != sorted(order_keys):\\n197: raise DemonovError(\\\"Bitfinex public trade history not ascending\\\")\\n198:\\n199: evidence_sha256 = canonical_hash(\\n200: {\\n201: \\\"schema\\\": \\\"CR0116_BITFINEX_RECONCILIATION_REST_EVIDENCE_V1\\\",\\n202: \\\"symbol\\\": symbol,\\n203: \\\"start_mts\\\": start_mts,\\n204: \\\"end_mts\\\": end_mts,\\n205: \\\"raw_row_count\\\": len(payload),\\n206: \\\"rows\\\": raw_rows,\\n207: }\\n208: )\\n209: return BitfinexTradeHistoryBatch(\\n210: symbol=symbol,\\n211: start_mts=start_mts,\\n212: end_mts=end_mts,\\n213: raw_row_count=len(payload),\\n214: trades=tuple(normalized),\\n215: evidence_sha256=evidence_sha256,\",\"source_sha256\":\"023ce0a041b50b866a48d9f752bd80479a34789d82f73c81d18246ed8871ef8e\",\"test_hits\":[\"126: journal.close()\\n127:\\n128:\\n129:def test_public_history_fetch_is_ascending_inclusive_and_deduplicates_exact_rows():\\n130: a, b, c = _trade(2001, 1), _trade(2002, 2), _trade(2003, 3)\\n131: calls = []\\n132: def fetch(url, *, timeout, max_bytes):\\n133: calls.append((url, timeout, max_bytes))\",\"133: calls.append((url, timeout, max_bytes))\\n134: return [_rest_row(a), _rest_row(b), _rest_row(b), _rest_row(c)]\\n135: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\\n136: batch = client.fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=c.mts)\\n137: assert [t.trade_id for t in batch.trades] == [2001, 2002, 2003]\\n138: assert batch.raw_row_count == 4\\n139: assert \\\"sort=1\\\" in calls[0][0] and \\\"limit=10000\\\" in calls[0][0]\\n140: assert f\\\"start={int(a.mts.timestamp()*1000)}\\\" in calls[0][0]\",\"145: a, b = _trade(2051, 1), _trade(2052, 2)\\n146: left = BitfinexPublicTradeHistoryClient(\\n147: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(a), _rest_row(b)]\\n148: ).fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n149: right = BitfinexPublicTradeHistoryClient(\\n150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\\n151: ).fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]\",\"148: ).fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n149: right = BitfinexPublicTradeHistoryClient(\\n150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\\n151: ).fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]\\n153: assert [t.trade_id for t in right.trades] == [2051, 2052]\\n154: assert left.raw_row_count == right.raw_row_count == 3\\n155: assert left.evidence_sha256 != right.evidence_sha256\",\"164: ]\\n165: for client in bad_clients:\\n166: with pytest.raises(DemonovError):\\n167: client.fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n168:\\n169:\\n170:def test_successful_reconciliation_recovers_middle_trade_and_is_idempotent(tmp_path):\\n171: journal, window, previous, next_trade = _journal_with_window(tmp_path)\",\"175: calls.append(url)\\n176: return [_rest_row(previous), _rest_row(middle), _rest_row(next_trade)]\\n177: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\\n178: resolved = client.reconcile_window(\\n179: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n180: )\\n181: repeated = client.reconcile_window(\\n182: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=21)\",\"178: resolved = client.reconcile_window(\\n179: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n180: )\\n181: repeated = client.reconcile_window(\\n182: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=21)\\n183: )\\n184: assert resolved.status is ReconciliationStatus.RECONCILED\\n185: assert resolved.recovered_row_count == 1\",\"189: journal.close()\\n190:\\n191:\\n192:def test_http_or_validation_failure_marks_window_unresolved(tmp_path):\\n193: journal, window, _, _ = _journal_with_window(tmp_path)\\n194: client = BitfinexPublicTradeHistoryClient(\\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\\\"simulated\\\"))\\n196: )\",\"194: client = BitfinexPublicTradeHistoryClient(\\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\\\"simulated\\\"))\\n196: )\\n197: resolved = client.reconcile_window(\\n198: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n199: )\\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n201: assert resolved.failure_reason == \\\"REST_FETCH_OR_VALIDATION_FAILED\\\"\",\"198: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n199: )\\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n201: assert resolved.failure_reason == \\\"REST_FETCH_OR_VALIDATION_FAILED\\\"\\n202: journal.close()\\n203:\\n204:\\n205:def test_exact_row_limit_marks_window_unresolved(tmp_path):\",\"212: rows[-1] = _rest_row(next_trade)\\n213: rows.sort(key=lambda r: (r[1], r[0]))\\n214: client = BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: rows)\\n215: resolved = client.reconcile_window(\\n216: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n217: )\\n218: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n219: assert resolved.failure_reason == \\\"REST_ROW_LIMIT_REACHED\\\"\",\"226: client = BitfinexPublicTradeHistoryClient(\\n227: fetch_json=lambda *a, **k: [_rest_row(previous), _rest_row(middle)]\\n228: )\\n229: resolved = client.reconcile_window(\\n230: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n231: )\\n232: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n233: assert resolved.failure_reason == \\\"NEXT_BOUNDARY_MISSING\\\"\",\"243: client = BitfinexPublicTradeHistoryClient(\\n244: fetch_json=lambda *a, **k: [conflicting, _rest_row(next_trade)]\\n245: )\\n246: resolved = client.reconcile_window(\\n247: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n248: )\\n249: after = journal._db.execute(\\n250: \\\"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\\\", (previous.trade_identity,)\"],\"test_lines_150_280\":\"150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\\n151: ).fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]\\n153: assert [t.trade_id for t in right.trades] == [2051, 2052]\\n154: assert left.raw_row_count == right.raw_row_count == 3\\n155: assert left.evidence_sha256 != right.evidence_sha256\\n156:\\n157:\\n158:def test_public_history_rejects_malformed_unordered_and_conflicting_duplicate_rows():\\n159: a, b = _trade(2101, 1), _trade(2102, 2)\\n160: bad_clients = [\\n161: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [[1, 2]]),\\n162: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [_rest_row(b), _rest_row(a)]),\\n163: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [_rest_row(a), [a.trade_id, int(a.mts.timestamp()*1000), \\\"0.5\\\", \\\"101\\\"]]),\\n164: ]\\n165: for client in bad_clients:\\n166: with pytest.raises(DemonovError):\\n167: client.fetch_trade_window(\\\"tBTCUSD\\\", start_mts=a.mts, end_mts=b.mts)\\n168:\\n169:\\n170:def test_successful_reconciliation_recovers_middle_trade_and_is_idempotent(tmp_path):\\n171: journal, window, previous, next_trade = _journal_with_window(tmp_path)\\n172: middle = _trade(1002, 7)\\n173: calls = []\\n174: def fetch(url, *, timeout, max_bytes):\\n175: calls.append(url)\\n176: return [_rest_row(previous), _rest_row(middle), _rest_row(next_trade)]\\n177: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\\n178: resolved = client.reconcile_window(\\n179: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n180: )\\n181: repeated = client.reconcile_window(\\n182: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=21)\\n183: )\\n184: assert resolved.status is ReconciliationStatus.RECONCILED\\n185: assert resolved.recovered_row_count == 1\\n186: assert journal.has_trade_identity(middle.trade_identity)\\n187: assert repeated == resolved\\n188: assert len(calls) == 1\\n189: journal.close()\\n190:\\n191:\\n192:def test_http_or_validation_failure_marks_window_unresolved(tmp_path):\\n193: journal, window, _, _ = _journal_with_window(tmp_path)\\n194: client = BitfinexPublicTradeHistoryClient(\\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\\\"simulated\\\"))\\n196: )\\n197: resolved = client.reconcile_window(\\n198: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n199: )\\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n201: assert resolved.failure_reason == \\\"REST_FETCH_OR_VALIDATION_FAILED\\\"\\n202: journal.close()\\n203:\\n204:\\n205:def test_exact_row_limit_marks_window_unresolved(tmp_path):\\n206: journal, window, previous, next_trade = _journal_with_window(tmp_path)\\n207: rows = []\\n208: for i in range(BITFINEX_PUBLIC_HISTORY_LIMIT):\\n209: trade_id = 300000 + i\\n210: rows.append([trade_id, int((previous.mts + timedelta(milliseconds=i)).timestamp()*1000), \\\"0.5\\\", \\\"100\\\"])\\n211: rows[0] = _rest_row(previous)\\n212: rows[-1] = _rest_row(next_trade)\\n213: rows.sort(key=lambda r: (r[1], r[0]))\\n214: client = BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: rows)\\n215: resolved = client.reconcile_window(\\n216: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n217: )\\n218: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n219: assert resolved.failure_reason == \\\"REST_ROW_LIMIT_REACHED\\\"\\n220: journal.close()\\n221:\\n222:\\n223:def test_missing_boundary_marks_window_unresolved(tmp_path):\\n224: journal, window, previous, _ = _journal_with_window(tmp_path, persist_next=False)\\n225: middle = _trade(1002, 7)\\n226: client = BitfinexPublicTradeHistoryClient(\\n227: fetch_json=lambda *a, **k: [_rest_row(previous), _rest_row(middle)]\\n228: )\\n229: resolved = client.reconcile_window(\\n230: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n231: )\\n232: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n233: assert resolved.failure_reason == \\\"NEXT_BOUNDARY_MISSING\\\"\\n234: journal.close()\\n235:\\n236:\\n237:def test_semantic_conflict_marks_window_unresolved_without_new_revision(tmp_path):\\n238: journal, window, previous, next_trade = _journal_with_window(tmp_path)\\n239: conflicting = [previous.trade_id, int(previous.mts.timestamp()*1000), \\\"0.5\\\", \\\"101\\\"]\\n240: before = journal._db.execute(\\n241: \\\"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\\\", (previous.trade_identity,)\\n242: ).fetchone()[\\\"n\\\"]\\n243: client = BitfinexPublicTradeHistoryClient(\\n244: fetch_json=lambda *a, **k: [conflicting, _rest_row(next_trade)]\\n245: )\\n246: resolved = client.reconcile_window(\\n247: journal, window.window_id, symbol=\\\"tBTCUSD\\\", resolved_at=T0 + timedelta(seconds=20)\\n248: )\\n249: after = journal._db.execute(\\n250: \\\"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\\\", (previous.trade_identity,)\\n251: ).fetchone()[\\\"n\\\"]\\n252: assert resolved.status is ReconciliationStatus.UNRESOLVED\\n253: assert resolved.failure_reason == \\\"REST_SEMANTIC_CONFLICT\\\"\\n254: assert after == before\\n255: journal.close()\",\"test_sha256\":\"bebdd9c2875d2c63a0ac27d89eefa7f1c36c83c067aa8b472c0616ac470dd2a2\"}\nTASK_RESULT=PASS_SIGNALBOT_052FIXRCA1_ROLLBACK_AND_TEST_SEMANTICS_CAPTURED\nHOMELAB_RESULT_CONTRACT={\"version\":1,\"command_id\":\"SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1\",\"status\":\"OK\",\"changes_made\":false,\"rollback_started\":false,\"rollback_restored\":null}\n" } diff --git a/runtime/latest.txt b/runtime/latest.txt index 3ed0dcd0..c31d43a0 100644 --- a/runtime/latest.txt +++ b/runtime/latest.txt @@ -1,64 +1,35 @@ CHAT_OUTPUT_BEGIN -COMMAND_ID=NEWFI-260921-NEWFDOM-MEDICAL218-FINAL347 +COMMAND_ID=SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1 STATUS=OK RC=0 HOST=pve01 -MODE=change -COMPONENT=newfi-medical218-private-owner-records-staging-production-git-final +MODE=read-only +COMPONENT=signalbot-order-normalization-test-rca REFERENCE_REGISTER_CHECK=OK REFERENCE_SHA256=5a3d8e5154c41cb582a4e0aca68090be1f0138918bf82131a948df326f9d8d66 ERROR_REGISTER_CHECK=OK ERROR_REGISTER_SHA256=3b09a553ec0f527ed3afeed4753f52a74ee3036045bbfb3c685e9f8af4ba7ba0 -COMMAND_SHA256=7c0be8f606fd9d478f81be16fc1479f945886af6c6557c9d912542fa88619385 +COMMAND_SHA256=bb6b4fbaa3c7a735965adc6a9e217dc7242ed3097f157c63cb7d11fcba77a0fa DUPLICATE_FAILED_COMMAND_BLOCKED=false EXECUTION_STARTED=true -CHANGE_DECLARED=true +CHANGE_DECLARED=false RESULT_CONTRACT_VALID=true -RESULT_CONTRACT_STATUS=OK +RESULT_CONTRACT_STATUS=NOT_APPLICABLE RESULT_CONTRACT_ERROR=NONE COMMAND_RC=0 -CHANGES_MADE=true +CHANGES_MADE=false ROLLBACK_STARTED=false ROLLBACK_RESTORED=null -MUTATION_OUTCOME=MUTATED +MUTATION_OUTCOME=NO_MUTATION SANITIZED=yes SECRETS_INCLUDED=no PRIVATE_ADDRESSES_INCLUDED=no -RAW_EVIDENCE_SHA256=d43a0dcda6732018020a05563f8cb16c17444852019be703abb05e596ec0c711 -SANITIZED_OUTPUT_SHA256=d43a0dcda6732018020a05563f8cb16c17444852019be703abb05e596ec0c711 +RAW_EVIDENCE_SHA256=6f146b4f6887dfacff94c79715d1e60e5cdb633c76d4b25a3f28feec05a11eab +SANITIZED_OUTPUT_SHA256=6f146b4f6887dfacff94c79715d1e60e5cdb633c76d4b25a3f28feec05a11eab OUTPUT_BEGIN -MED347_BEGIN=true -COMMAND_ID=NEWFI-260921-NEWFDOM-MEDICAL218-FINAL347 -IMMUTABLE_PLAN_CHECK=PASS -GIT_PRESTATE={"branch":"implementation/newfi-closed-mvp-20260906","clean":true,"head":"33b6f020b8c7bd6adc57f51c164ff028aadc62cc","remote":"33b6f020b8c7bd6adc57f51c164ff028aadc62cc"} -CANDIDATE={"documents":"PRIVATE_HTTPS_REFERENCE","files":{"Pub/Controller/Medical.php":"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f","Pub/View/Medical.php":"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d","Service/Medical.php":"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283","Setup.php":"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a","_data/routes.xml":"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6","_data/templates.xml":"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2","addon.json":"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384"},"fixed_bundle":"cb6bcf8c661bd0a8ee1cda229a67e5983f4f0925d5481fc12e554d9bef529711","old_dogs_sha":"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5","patched_dogs_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","privacy":"OWNER_OR_ADMIN_ONLY","reminders":"RECORD_DUE_STATE_NO_ALERT_SERVICE"} -PUBLIC_PRE={"/":"200","/dogs/":"200","/events/":"200","/map/":"200","/medical/":"404","/pedigree/":"200"} -VM211_PREFLIGHT={"action":"preflight","result":{"addon":null,"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":false,"dogs_rows":0,"dogs_tpl_sha":"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5","errors_count":29,"errors_fp":"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db","errors_max":61,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":0,"rows":0,"source_exists":false,"source_manifest":{},"table":0,"templates":0},"schema_version":1,"status":"PASS"} -VM210_PREFLIGHT={"action":"preflight","result":{"addon":null,"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":false,"dogs_rows":0,"dogs_tpl_sha":"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5","errors_count":46,"errors_fp":"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d","errors_max":46,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":0,"rows":0,"source_exists":false,"source_manifest":{},"table":0,"templates":0},"schema_version":1,"status":"PASS"} -VM211_APPLY={"action":"apply","result":{"fixed_bundle":"cb6bcf8c661bd0a8ee1cda229a67e5983f4f0925d5481fc12e554d9bef529711","meta":{"old_dogs_sha":"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5","patched_dogs_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","pre":{"addon":null,"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":false,"dogs_rows":0,"dogs_tpl_sha":"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5","errors_count":29,"errors_fp":"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db","errors_max":61,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":0,"rows":0,"source_exists":false,"source_manifest":{},"table":0,"templates":0}},"state":{"addon":{"active":1,"addon_id":"Newfi/Medical","is_processing":0,"version_id":1000010},"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":true,"dogs_rows":0,"dogs_tpl_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","errors_count":29,"errors_fp":"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db","errors_max":61,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":1,"rows":0,"source_exists":true,"source_manifest":{"Pub/Controller/Medical.php":"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f","Pub/View/Medical.php":"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d","Service/Medical.php":"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283","Setup.php":"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a","_data/routes.xml":"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6","_data/templates.xml":"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2","addon.json":"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384"},"table":1,"templates":3}},"schema_version":1,"status":"PASS"} -VM211_SYNTHETIC={"action":"synthetic","result":{"state":{"addon":{"active":1,"addon_id":"Newfi/Medical","is_processing":0,"version_id":1000010},"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":true,"dogs_rows":0,"dogs_tpl_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","errors_count":29,"errors_fp":"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db","errors_max":61,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":1,"rows":0,"source_exists":true,"source_manifest":{"Pub/Controller/Medical.php":"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f","Pub/View/Medical.php":"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d","Service/Medical.php":"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283","Setup.php":"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a","_data/routes.xml":"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6","_data/templates.xml":"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2","addon.json":"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384"},"table":1,"templates":3},"synthetic":{"delete":true,"dog_rows_after":0,"guest_denied":true,"https_document_validation":true,"medical_rows_after":0,"owner_policy":true,"private_template":true,"record_types":true,"records_during":5,"reminder_state":true,"weight_history":true}},"schema_version":1,"status":"PASS"} -VM211_RESTORE={"action":"restore","result":{"addon":null,"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":false,"dogs_rows":0,"dogs_tpl_sha":"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5","errors_count":29,"errors_fp":"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db","errors_max":61,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":0,"rows":0,"source_exists":false,"source_manifest":{},"table":0,"templates":0},"schema_version":1,"status":"PASS"} -VM211_APPLY={"action":"apply","result":{"fixed_bundle":"cb6bcf8c661bd0a8ee1cda229a67e5983f4f0925d5481fc12e554d9bef529711","meta":{"old_dogs_sha":"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5","patched_dogs_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","pre":{"addon":null,"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":false,"dogs_rows":0,"dogs_tpl_sha":"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5","errors_count":29,"errors_fp":"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db","errors_max":61,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":0,"rows":0,"source_exists":false,"source_manifest":{},"table":0,"templates":0}},"state":{"addon":{"active":1,"addon_id":"Newfi/Medical","is_processing":0,"version_id":1000010},"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":true,"dogs_rows":0,"dogs_tpl_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","errors_count":29,"errors_fp":"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db","errors_max":61,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":1,"rows":0,"source_exists":true,"source_manifest":{"Pub/Controller/Medical.php":"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f","Pub/View/Medical.php":"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d","Service/Medical.php":"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283","Setup.php":"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a","_data/routes.xml":"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6","_data/templates.xml":"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2","addon.json":"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384"},"table":1,"templates":3}},"schema_version":1,"status":"PASS"} -VM211_SYNTHETIC={"action":"synthetic","result":{"state":{"addon":{"active":1,"addon_id":"Newfi/Medical","is_processing":0,"version_id":1000010},"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":true,"dogs_rows":0,"dogs_tpl_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","errors_count":29,"errors_fp":"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db","errors_max":61,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":1,"rows":0,"source_exists":true,"source_manifest":{"Pub/Controller/Medical.php":"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f","Pub/View/Medical.php":"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d","Service/Medical.php":"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283","Setup.php":"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a","_data/routes.xml":"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6","_data/templates.xml":"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2","addon.json":"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384"},"table":1,"templates":3},"synthetic":{"delete":true,"dog_rows_after":0,"guest_denied":true,"https_document_validation":true,"medical_rows_after":0,"owner_policy":true,"private_template":true,"record_types":true,"records_during":5,"reminder_state":true,"weight_history":true}},"schema_version":1,"status":"PASS"} -VM211_AUDIT={"action":"audit","result":{"addon":{"active":1,"addon_id":"Newfi/Medical","is_processing":0,"version_id":1000010},"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":true,"dogs_rows":0,"dogs_tpl_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","errors_count":29,"errors_fp":"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db","errors_max":61,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":1,"rows":0,"source_exists":true,"source_manifest":{"Pub/Controller/Medical.php":"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f","Pub/View/Medical.php":"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d","Service/Medical.php":"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283","Setup.php":"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a","_data/routes.xml":"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6","_data/templates.xml":"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2","addon.json":"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384"},"table":1,"templates":3},"schema_version":1,"status":"PASS"} -VM210_APPLY={"action":"apply","result":{"fixed_bundle":"cb6bcf8c661bd0a8ee1cda229a67e5983f4f0925d5481fc12e554d9bef529711","meta":{"old_dogs_sha":"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5","patched_dogs_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","pre":{"addon":null,"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":false,"dogs_rows":0,"dogs_tpl_sha":"18a27d937a4e793451500c93bb91823b89136bc820ecb61b04ffdcb3f9044ba5","errors_count":46,"errors_fp":"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d","errors_max":46,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":0,"rows":0,"source_exists":false,"source_manifest":{},"table":0,"templates":0}},"state":{"addon":{"active":1,"addon_id":"Newfi/Medical","is_processing":0,"version_id":1000010},"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":true,"dogs_rows":0,"dogs_tpl_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","errors_count":46,"errors_fp":"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d","errors_max":46,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":1,"rows":0,"source_exists":true,"source_manifest":{"Pub/Controller/Medical.php":"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f","Pub/View/Medical.php":"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d","Service/Medical.php":"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283","Setup.php":"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a","_data/routes.xml":"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6","_data/templates.xml":"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2","addon.json":"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384"},"table":1,"templates":3}},"schema_version":1,"status":"PASS"} -VM210_AUDIT={"action":"audit","result":{"addon":{"active":1,"addon_id":"Newfi/Medical","is_processing":0,"version_id":1000010},"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":true,"dogs_rows":0,"dogs_tpl_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","errors_count":46,"errors_fp":"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d","errors_max":46,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":1,"rows":0,"source_exists":true,"source_manifest":{"Pub/Controller/Medical.php":"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f","Pub/View/Medical.php":"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d","Service/Medical.php":"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283","Setup.php":"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a","_data/routes.xml":"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6","_data/templates.xml":"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2","addon.json":"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384"},"table":1,"templates":3},"schema_version":1,"status":"PASS"} -PUBLIC_PRIVACY={"base":{"/":"200","/dogs/":"200","/events/":"200","/map/":"200","/pedigree/":"200"},"fake_bytes":26312,"fake_http":"404","fake_stderr_sha":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","medical_bytes":30366,"medical_http":"403","medical_stderr_sha":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","private_marker_fake":false,"private_marker_medical":false} -VM210_AUDIT={"action":"audit","result":{"addon":{"active":1,"addon_id":"Newfi/Medical","is_processing":0,"version_id":1000010},"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":true,"dogs_rows":0,"dogs_tpl_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","errors_count":46,"errors_fp":"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d","errors_max":46,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":1,"rows":0,"source_exists":true,"source_manifest":{"Pub/Controller/Medical.php":"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f","Pub/View/Medical.php":"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d","Service/Medical.php":"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283","Setup.php":"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a","_data/routes.xml":"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6","_data/templates.xml":"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2","addon.json":"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384"},"table":1,"templates":3},"schema_version":1,"status":"PASS"} -GIT_CANDIDATE_HEAD=8ee9246024eabf84efbf21dc9b02ae2a666dca26 -GIT_PUSH_RESULT={"rc":0,"remote_head":"8ee9246024eabf84efbf21dc9b02ae2a666dca26","stderr_sha":"1943a695309bf7e7dcc9ef182d4e1178117cd6602f683618a6e4df58a754ffd3","stdout_sha":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"} -GIT_FINAL={"branch":"implementation/newfi-closed-mvp-20260906","clean":true,"head":"8ee9246024eabf84efbf21dc9b02ae2a666dca26","remote":"8ee9246024eabf84efbf21dc9b02ae2a666dca26"} -VM211_AUDIT={"action":"audit","result":{"addon":{"active":1,"addon_id":"Newfi/Medical","is_processing":0,"version_id":1000010},"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":true,"dogs_rows":0,"dogs_tpl_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","errors_count":29,"errors_fp":"03fc811577f91849e5f8bc2e9be366ed0bbb99f7a779476af7ed03efa97461db","errors_max":61,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":1,"rows":0,"source_exists":true,"source_manifest":{"Pub/Controller/Medical.php":"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f","Pub/View/Medical.php":"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d","Service/Medical.php":"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283","Setup.php":"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a","_data/routes.xml":"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6","_data/templates.xml":"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2","addon.json":"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384"},"table":1,"templates":3},"schema_version":1,"status":"PASS"} -VM210_AUDIT={"action":"audit","result":{"addon":{"active":1,"addon_id":"Newfi/Medical","is_processing":0,"version_id":1000010},"dog_policy_class":true,"dogs_addon":{"active":1,"addon_id":"Newfi/Dogs","is_processing":0,"version_id":1000010},"dogs_medical_link_source":true,"dogs_rows":0,"dogs_tpl_sha":"279806b79661a65fb3a6f75967f39496ada443f8c2a7fb8a8495f25915fc0876","errors_count":46,"errors_fp":"23926bc6e73d3858316e0a3d008c0d3340fd552f2a22132c20e5ec0dded16c1d","errors_max":46,"pedigree_addon":{"active":1,"addon_id":"Newfi/Pedigree","is_processing":0,"version_id":1000010},"pedigree_rows":0,"route":1,"rows":0,"source_exists":true,"source_manifest":{"Pub/Controller/Medical.php":"86bff1fde408dcc61c6a3bc12ce2afc5d4466e738711288868873f3aed841d9f","Pub/View/Medical.php":"4832c23c2d0adae1f9ef454211a5a48b8643443cf5fb123afc9786c2887a384d","Service/Medical.php":"4c238d60f3913921507edc6223453be5c6bb779856ce37fc33b724f875c96283","Setup.php":"e07b8d1a671046cfd72bbd490d37759a8a9089ad81858ab9c63107848b83017a","_data/routes.xml":"e05f21d460cf9b3b83610c7d8c86965293c447f874d84235c84fbb87fc87c9c6","_data/templates.xml":"4616a52f4d34b96f32b31a047bcae389f0fb619b5db97c0777662ce32c010bf2","addon.json":"8d4b6fafca9cc3a37c3d7e650e35325808f85a3fcb883385fc5ccb1bc30cf384"},"table":1,"templates":3},"schema_version":1,"status":"PASS"} -PUBLIC_FINAL={"base":{"/":"200","/dogs/":"200","/events/":"200","/map/":"200","/pedigree/":"200"},"medical_http":"403","private_marker":false} -MEDICAL218_CLOSED=True -DECISION=PASS_MEDICAL218_PRIVATE_RECORDS_PRODUCTION_GIT_FINAL -NEXT_GATE=KENNELS219 -ACTIVE_MAJOR_STAGE=KENNELS219 -MAJOR_STAGES_REMAINING=3 -GIT_NEW_HEAD=8ee9246024eabf84efbf21dc9b02ae2a666dca26 -HOMELAB_RESULT_CONTRACT={"changes_made":true,"command_id":"NEWFI-260921-NEWFDOM-MEDICAL218-FINAL347","rollback_restored":null,"rollback_started":false,"status":"OK","version":1} -MED347_END=true +SIGNALBOT052FIXRCA1_RESULT={"branch":"signalbot-260917-reconnect-reconciliation","clean":true,"diff_empty":true,"head":"749073ae0f894debc06fb0ffbf7a84abf9da531f","package_identity":"9dd3a15343a59007aa1c0484f9a52b5987c67478687f1a5d3e3c91c7b95a9073","restored_exact":true,"source_lines_146_215":"146:\n147: def fetch_trade_window(\n148: self,\n149: symbol: str,\n150: *,\n151: start_mts: datetime,\n152: end_mts: datetime,\n153: ) -> BitfinexTradeHistoryBatch:\n154: if not isinstance(symbol, str) or not symbol.startswith(\"t\") or len(symbol) < 2:\n155: raise DemonovError(\"Bitfinex trade history symbol invalid\")\n156: start_mts = _utc(start_mts, \"start_mts\")\n157: end_mts = _utc(end_mts, \"end_mts\")\n158: if end_mts < start_mts:\n159: raise DemonovError(\"Bitfinex trade history interval invalid\")\n160: start_ms = int(start_mts.timestamp() * 1000)\n161: end_ms = int(end_mts.timestamp() * 1000)\n162: query = urlencode({\"start\": start_ms, \"end\": end_ms, \"sort\": 1, \"limit\": BITFINEX_PUBLIC_HISTORY_LIMIT})\n163: url = f\"{BITFINEX_PUBLIC_TRADES_BASE}/{quote(symbol, safe='')}/hist?{query}\"\n164: try:\n165: payload = self._fetch_json(\n166: url,\n167: timeout=self.timeout,\n168: max_bytes=self.max_bytes,\n169: )\n170: except DemonovError:\n171: raise\n172: except Exception as exc:\n173: raise DemonovError(\n174: \"Bitfinex public trade history request failed\"\n175: ) from exc\n176: if not isinstance(payload, list):\n177: raise DemonovError(\"Bitfinex public trade history payload invalid\")\n178:\n179: normalized: list[BitfinexCanonicalTradeV1] = []\n180: raw_rows: list[list[object]] = []\n181: seen: dict[str, str] = {}\n182: order_keys: list[tuple[datetime, int]] = []\n183: for raw in payload:\n184: if not isinstance(raw, list):\n185: raise DemonovError(\"Bitfinex REST trade row malformed\")\n186: trade = normalize_bitfinex_rest_trade(raw)\n187: raw_rows.append(list(raw[:4]))\n188: prior = seen.get(trade.trade_identity)\n189: if prior is not None:\n190: if prior != trade.semantic_hash:\n191: raise DemonovError(\"Bitfinex REST trade semantic conflict\")\n192: continue\n193: seen[trade.trade_identity] = trade.semantic_hash\n194: normalized.append(trade)\n195: order_keys.append((trade.mts, trade.trade_id))\n196: if order_keys != sorted(order_keys):\n197: raise DemonovError(\"Bitfinex public trade history not ascending\")\n198:\n199: evidence_sha256 = canonical_hash(\n200: {\n201: \"schema\": \"CR0116_BITFINEX_RECONCILIATION_REST_EVIDENCE_V1\",\n202: \"symbol\": symbol,\n203: \"start_mts\": start_mts,\n204: \"end_mts\": end_mts,\n205: \"raw_row_count\": len(payload),\n206: \"rows\": raw_rows,\n207: }\n208: )\n209: return BitfinexTradeHistoryBatch(\n210: symbol=symbol,\n211: start_mts=start_mts,\n212: end_mts=end_mts,\n213: raw_row_count=len(payload),\n214: trades=tuple(normalized),\n215: evidence_sha256=evidence_sha256,","source_sha256":"023ce0a041b50b866a48d9f752bd80479a34789d82f73c81d18246ed8871ef8e","test_hits":["126: journal.close()\n127:\n128:\n129:def test_public_history_fetch_is_ascending_inclusive_and_deduplicates_exact_rows():\n130: a, b, c = _trade(2001, 1), _trade(2002, 2), _trade(2003, 3)\n131: calls = []\n132: def fetch(url, *, timeout, max_bytes):\n133: calls.append((url, timeout, max_bytes))","133: calls.append((url, timeout, max_bytes))\n134: return [_rest_row(a), _rest_row(b), _rest_row(b), _rest_row(c)]\n135: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\n136: batch = client.fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=c.mts)\n137: assert [t.trade_id for t in batch.trades] == [2001, 2002, 2003]\n138: assert batch.raw_row_count == 4\n139: assert \"sort=1\" in calls[0][0] and \"limit=10000\" in calls[0][0]\n140: assert f\"start={int(a.mts.timestamp()*1000)}\" in calls[0][0]","145: a, b = _trade(2051, 1), _trade(2052, 2)\n146: left = BitfinexPublicTradeHistoryClient(\n147: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(a), _rest_row(b)]\n148: ).fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n149: right = BitfinexPublicTradeHistoryClient(\n150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\n151: ).fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]","148: ).fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n149: right = BitfinexPublicTradeHistoryClient(\n150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\n151: ).fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]\n153: assert [t.trade_id for t in right.trades] == [2051, 2052]\n154: assert left.raw_row_count == right.raw_row_count == 3\n155: assert left.evidence_sha256 != right.evidence_sha256","164: ]\n165: for client in bad_clients:\n166: with pytest.raises(DemonovError):\n167: client.fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n168:\n169:\n170:def test_successful_reconciliation_recovers_middle_trade_and_is_idempotent(tmp_path):\n171: journal, window, previous, next_trade = _journal_with_window(tmp_path)","175: calls.append(url)\n176: return [_rest_row(previous), _rest_row(middle), _rest_row(next_trade)]\n177: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\n178: resolved = client.reconcile_window(\n179: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n180: )\n181: repeated = client.reconcile_window(\n182: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=21)","178: resolved = client.reconcile_window(\n179: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n180: )\n181: repeated = client.reconcile_window(\n182: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=21)\n183: )\n184: assert resolved.status is ReconciliationStatus.RECONCILED\n185: assert resolved.recovered_row_count == 1","189: journal.close()\n190:\n191:\n192:def test_http_or_validation_failure_marks_window_unresolved(tmp_path):\n193: journal, window, _, _ = _journal_with_window(tmp_path)\n194: client = BitfinexPublicTradeHistoryClient(\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\"simulated\"))\n196: )","194: client = BitfinexPublicTradeHistoryClient(\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\"simulated\"))\n196: )\n197: resolved = client.reconcile_window(\n198: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n199: )\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\n201: assert resolved.failure_reason == \"REST_FETCH_OR_VALIDATION_FAILED\"","198: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n199: )\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\n201: assert resolved.failure_reason == \"REST_FETCH_OR_VALIDATION_FAILED\"\n202: journal.close()\n203:\n204:\n205:def test_exact_row_limit_marks_window_unresolved(tmp_path):","212: rows[-1] = _rest_row(next_trade)\n213: rows.sort(key=lambda r: (r[1], r[0]))\n214: client = BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: rows)\n215: resolved = client.reconcile_window(\n216: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n217: )\n218: assert resolved.status is ReconciliationStatus.UNRESOLVED\n219: assert resolved.failure_reason == \"REST_ROW_LIMIT_REACHED\"","226: client = BitfinexPublicTradeHistoryClient(\n227: fetch_json=lambda *a, **k: [_rest_row(previous), _rest_row(middle)]\n228: )\n229: resolved = client.reconcile_window(\n230: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n231: )\n232: assert resolved.status is ReconciliationStatus.UNRESOLVED\n233: assert resolved.failure_reason == \"NEXT_BOUNDARY_MISSING\"","243: client = BitfinexPublicTradeHistoryClient(\n244: fetch_json=lambda *a, **k: [conflicting, _rest_row(next_trade)]\n245: )\n246: resolved = client.reconcile_window(\n247: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n248: )\n249: after = journal._db.execute(\n250: \"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\", (previous.trade_identity,)"],"test_lines_150_280":"150: fetch_json=lambda *args, **kwargs: [_rest_row(a), _rest_row(b), _rest_row(b)]\n151: ).fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n152: assert [t.trade_id for t in left.trades] == [2051, 2052]\n153: assert [t.trade_id for t in right.trades] == [2051, 2052]\n154: assert left.raw_row_count == right.raw_row_count == 3\n155: assert left.evidence_sha256 != right.evidence_sha256\n156:\n157:\n158:def test_public_history_rejects_malformed_unordered_and_conflicting_duplicate_rows():\n159: a, b = _trade(2101, 1), _trade(2102, 2)\n160: bad_clients = [\n161: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [[1, 2]]),\n162: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [_rest_row(b), _rest_row(a)]),\n163: BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: [_rest_row(a), [a.trade_id, int(a.mts.timestamp()*1000), \"0.5\", \"101\"]]),\n164: ]\n165: for client in bad_clients:\n166: with pytest.raises(DemonovError):\n167: client.fetch_trade_window(\"tBTCUSD\", start_mts=a.mts, end_mts=b.mts)\n168:\n169:\n170:def test_successful_reconciliation_recovers_middle_trade_and_is_idempotent(tmp_path):\n171: journal, window, previous, next_trade = _journal_with_window(tmp_path)\n172: middle = _trade(1002, 7)\n173: calls = []\n174: def fetch(url, *, timeout, max_bytes):\n175: calls.append(url)\n176: return [_rest_row(previous), _rest_row(middle), _rest_row(next_trade)]\n177: client = BitfinexPublicTradeHistoryClient(fetch_json=fetch)\n178: resolved = client.reconcile_window(\n179: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n180: )\n181: repeated = client.reconcile_window(\n182: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=21)\n183: )\n184: assert resolved.status is ReconciliationStatus.RECONCILED\n185: assert resolved.recovered_row_count == 1\n186: assert journal.has_trade_identity(middle.trade_identity)\n187: assert repeated == resolved\n188: assert len(calls) == 1\n189: journal.close()\n190:\n191:\n192:def test_http_or_validation_failure_marks_window_unresolved(tmp_path):\n193: journal, window, _, _ = _journal_with_window(tmp_path)\n194: client = BitfinexPublicTradeHistoryClient(\n195: fetch_json=lambda *a, **k: (_ for _ in ()).throw(TimeoutError(\"simulated\"))\n196: )\n197: resolved = client.reconcile_window(\n198: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n199: )\n200: assert resolved.status is ReconciliationStatus.UNRESOLVED\n201: assert resolved.failure_reason == \"REST_FETCH_OR_VALIDATION_FAILED\"\n202: journal.close()\n203:\n204:\n205:def test_exact_row_limit_marks_window_unresolved(tmp_path):\n206: journal, window, previous, next_trade = _journal_with_window(tmp_path)\n207: rows = []\n208: for i in range(BITFINEX_PUBLIC_HISTORY_LIMIT):\n209: trade_id = 300000 + i\n210: rows.append([trade_id, int((previous.mts + timedelta(milliseconds=i)).timestamp()*1000), \"0.5\", \"100\"])\n211: rows[0] = _rest_row(previous)\n212: rows[-1] = _rest_row(next_trade)\n213: rows.sort(key=lambda r: (r[1], r[0]))\n214: client = BitfinexPublicTradeHistoryClient(fetch_json=lambda *a, **k: rows)\n215: resolved = client.reconcile_window(\n216: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n217: )\n218: assert resolved.status is ReconciliationStatus.UNRESOLVED\n219: assert resolved.failure_reason == \"REST_ROW_LIMIT_REACHED\"\n220: journal.close()\n221:\n222:\n223:def test_missing_boundary_marks_window_unresolved(tmp_path):\n224: journal, window, previous, _ = _journal_with_window(tmp_path, persist_next=False)\n225: middle = _trade(1002, 7)\n226: client = BitfinexPublicTradeHistoryClient(\n227: fetch_json=lambda *a, **k: [_rest_row(previous), _rest_row(middle)]\n228: )\n229: resolved = client.reconcile_window(\n230: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n231: )\n232: assert resolved.status is ReconciliationStatus.UNRESOLVED\n233: assert resolved.failure_reason == \"NEXT_BOUNDARY_MISSING\"\n234: journal.close()\n235:\n236:\n237:def test_semantic_conflict_marks_window_unresolved_without_new_revision(tmp_path):\n238: journal, window, previous, next_trade = _journal_with_window(tmp_path)\n239: conflicting = [previous.trade_id, int(previous.mts.timestamp()*1000), \"0.5\", \"101\"]\n240: before = journal._db.execute(\n241: \"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\", (previous.trade_identity,)\n242: ).fetchone()[\"n\"]\n243: client = BitfinexPublicTradeHistoryClient(\n244: fetch_json=lambda *a, **k: [conflicting, _rest_row(next_trade)]\n245: )\n246: resolved = client.reconcile_window(\n247: journal, window.window_id, symbol=\"tBTCUSD\", resolved_at=T0 + timedelta(seconds=20)\n248: )\n249: after = journal._db.execute(\n250: \"SELECT COUNT(*) AS n FROM trade_revisions WHERE trade_identity=?\", (previous.trade_identity,)\n251: ).fetchone()[\"n\"]\n252: assert resolved.status is ReconciliationStatus.UNRESOLVED\n253: assert resolved.failure_reason == \"REST_SEMANTIC_CONFLICT\"\n254: assert after == before\n255: journal.close()","test_sha256":"bebdd9c2875d2c63a0ac27d89eefa7f1c36c83c067aa8b472c0616ac470dd2a2"} +TASK_RESULT=PASS_SIGNALBOT_052FIXRCA1_ROLLBACK_AND_TEST_SEMANTICS_CAPTURED +HOMELAB_RESULT_CONTRACT={"version":1,"command_id":"SIGNALBOT-260921-ORDER-NORMALIZATION-TEST-RCA-052FIXRCA1","status":"OK","changes_made":false,"rollback_started":false,"rollback_restored":null} OUTPUT_END CHAT_OUTPUT_END