diff --git a/pyproject.toml b/pyproject.toml
index cace5e4..0660b5a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "plba"
-version = "0.5.0"
+version = "0.5.1"
description = "Platform runtime for business applications"
readme = "README.md"
requires-python = ">=3.11"
diff --git a/src/app_runtime/contracts/trace.py b/src/app_runtime/contracts/trace.py
index a074d9d..09a1bf3 100644
--- a/src/app_runtime/contracts/trace.py
+++ b/src/app_runtime/contracts/trace.py
@@ -105,6 +105,6 @@ class TraceLogReader(Protocol):
levels: tuple[TraceLevel, ...],
ancestor_depth: int | None = 0,
*,
- view: Literal["tree", "single"] = "tree",
+ view: Literal["tree", "single"] = "single",
) -> TraceLogView | None:
"""Load trace context and filtered log records."""
diff --git a/src/app_runtime/control/base.py b/src/app_runtime/control/base.py
index 8deefb3..b41c985 100644
--- a/src/app_runtime/control/base.py
+++ b/src/app_runtime/control/base.py
@@ -29,7 +29,7 @@ class TraceQueryRequest:
include_attrs_json: bool = False
response_format: TraceResponseFormat = "html"
ancestor_depth: int | None = None
- view: TraceViewMode = "tree"
+ view: TraceViewMode = "single"
TraceLookupHandler = Callable[[str, TraceQueryRequest], Awaitable[TraceLogView]]
diff --git a/src/app_runtime/control/trace_presenter.py b/src/app_runtime/control/trace_presenter.py
index 6d12056..6932919 100644
--- a/src/app_runtime/control/trace_presenter.py
+++ b/src/app_runtime/control/trace_presenter.py
@@ -21,7 +21,7 @@ class TraceRequestParser:
response_format = raw_format.strip().lower()
if response_format not in {"json", "text", "html"}:
raise ValueError(f"unsupported trace format: {raw_format}")
- raw_view = request.query_params.get("view", "tree")
+ raw_view = request.query_params.get("view", "single")
view = raw_view.strip().lower()
if view not in {"tree", "single"}:
raise ValueError(f"unsupported trace view: {raw_view}")
diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-1.html
new file mode 100644
index 0000000..10c3b99
--- /dev/null
+++ b/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-1.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Trace 3afa2bd3-db74-433b-b852-4be5bdf2c60b
+
+
+
+
+
parent_id:
child_ids:
==============================
email-processing
Новых писем не найдено., {}
+
+
+
\ No newline at end of file
diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-2.html b/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-2.html
new file mode 100644
index 0000000..10c3b99
--- /dev/null
+++ b/tests/artifacts/trace_logs/tests-test_app.py-test_email_processing_empty_inbox_records_no_new_emails_message-2.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Trace 3afa2bd3-db74-433b-b852-4be5bdf2c60b
+
+
+
+
+
parent_id:
child_ids:
==============================
email-processing
Новых писем не найдено., {}
+
+
+
\ No newline at end of file
diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_health_endpoint_exposes_components-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_health_endpoint_exposes_components-1.html
new file mode 100644
index 0000000..a46dbd3
--- /dev/null
+++ b/tests/artifacts/trace_logs/tests-test_app.py-test_health_endpoint_exposes_components-1.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Trace dc5769f8-4451-4acc-a923-0de56283285e
+
+
+
+
+
parent_id:
child_ids:
==============================
order-sync
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-08-11T20:10:04.926255+00:00, period_end=2026-08-12T20:10:04.926255+00:00., {"period_start":"2026-08-11T20:10:04.926255+00:00","period_end":"2026-08-12T20:10:04.926255+00:00","abcp_request_url":"static://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-08-12T20:10:04.924987+00:00, period_end=2026-08-12T20:10:04.926255+00:00., {"period_start":"2026-08-12T20:10:04.924987+00:00","period_end":"2026-08-12T20:10:04.926255+00:00","abcp_request_url":"static://cp/orders"}
Синхронизация заказов завершена. records_loaded=1, period_start=2026-08-11T20:10:04.926255+00:00, period_end=2026-08-12T20:10:04.926255+00:00., {"records_loaded":1,"period_start":"2026-08-11T20:10:04.926255+00:00","period_end":"2026-08-12T20:10:04.926255+00:00"}
+
+
+
\ No newline at end of file
diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_loads_multiple_batches_until_sync_window_is_closed-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_loads_multiple_batches_until_sync_window_is_closed-1.html
new file mode 100644
index 0000000..f1cc153
--- /dev/null
+++ b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_loads_multiple_batches_until_sync_window_is_closed-1.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Trace c877db56-2148-47d0-9cb8-c8366ecb094a
+
+
+
+
+
parent_id:
child_ids:
==============================
order-sync
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-04-14T20:10:04.612913+00:00, period_end=2026-08-12T20:10:04.612913+00:00., {"period_start":"2026-04-14T20:10:04.612913+00:00","period_end":"2026-08-12T20:10:04.612913+00:00","abcp_request_url":"unknown://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-03-24T15:36:42+00:00, period_end=2026-08-12T20:10:04.612913+00:00., {"period_start":"2026-03-24T15:36:42+00:00","period_end":"2026-08-12T20:10:04.612913+00:00","abcp_request_url":"unknown://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-04-02T09:30:00+00:00, period_end=2026-08-12T20:10:04.612913+00:00., {"period_start":"2026-04-02T09:30:00+00:00","period_end":"2026-08-12T20:10:04.612913+00:00","abcp_request_url":"unknown://cp/orders"}
Синхронизация заказов завершена. records_loaded=3, period_start=2026-04-14T20:10:04.612913+00:00, period_end=2026-08-12T20:10:04.612913+00:00., {"records_loaded":3,"period_start":"2026-04-14T20:10:04.612913+00:00","period_end":"2026-08-12T20:10:04.612913+00:00"}
+
+
+
\ No newline at end of file
diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_populates_snapshot-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_populates_snapshot-1.html
new file mode 100644
index 0000000..3d67d4f
--- /dev/null
+++ b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_populates_snapshot-1.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Trace 78ed0674-e1fa-495e-a8f7-2d1eb189a4b2
+
+
+
+
+
parent_id:
child_ids:
==============================
order-sync
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-08-11T20:10:04.195237+00:00, period_end=2026-08-12T20:10:04.195237+00:00., {"period_start":"2026-08-11T20:10:04.195237+00:00","period_end":"2026-08-12T20:10:04.195237+00:00","abcp_request_url":"static://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-08-12T20:10:04.193492+00:00, period_end=2026-08-12T20:10:04.195237+00:00., {"period_start":"2026-08-12T20:10:04.193492+00:00","period_end":"2026-08-12T20:10:04.195237+00:00","abcp_request_url":"static://cp/orders"}
Синхронизация заказов завершена. records_loaded=1, period_start=2026-08-11T20:10:04.195237+00:00, period_end=2026-08-12T20:10:04.195237+00:00., {"records_loaded":1,"period_start":"2026-08-11T20:10:04.195237+00:00","period_end":"2026-08-12T20:10:04.195237+00:00"}
+
+
+
\ No newline at end of file
diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_rolling_lookback_ignores_existing_last_sync_time-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_rolling_lookback_ignores_existing_last_sync_time-1.html
new file mode 100644
index 0000000..12ae9b7
--- /dev/null
+++ b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_rolling_lookback_ignores_existing_last_sync_time-1.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Trace 3ec474af-77db-4d4e-82cc-0c9df0ae10c3
+
+
+
+
+
parent_id:
child_ids:
==============================
order-sync
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-04-14T20:10:04.605396+00:00, period_end=2026-08-12T20:10:04.605396+00:00., {"period_start":"2026-04-14T20:10:04.605396+00:00","period_end":"2026-08-12T20:10:04.605396+00:00","abcp_request_url":"unknown://cp/orders"}
Синхронизация заказов завершена без изменений. records_loaded=0, period_start=2026-04-14T20:10:04.605396+00:00, period_end=2026-08-12T20:10:04.605396+00:00., {"records_loaded":0,"period_start":"2026-04-14T20:10:04.605396+00:00","period_end":"2026-08-12T20:10:04.605396+00:00"}
+
+
+
\ No newline at end of file
diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_waits_between_followup_abcp_requests-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_waits_between_followup_abcp_requests-1.html
new file mode 100644
index 0000000..b44d7f7
--- /dev/null
+++ b/tests/artifacts/trace_logs/tests-test_app.py-test_order_sync_waits_between_followup_abcp_requests-1.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Trace c263b805-227c-4faf-855b-2d7bc51427e6
+
+
+
+
+
parent_id:
child_ids:
==============================
order-sync
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-04-14T20:10:04.620037+00:00, period_end=2026-08-12T20:10:04.620037+00:00., {"period_start":"2026-04-14T20:10:04.620037+00:00","period_end":"2026-08-12T20:10:04.620037+00:00","abcp_request_url":"unknown://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-03-24T15:36:42+00:00, period_end=2026-08-12T20:10:04.620037+00:00., {"period_start":"2026-03-24T15:36:42+00:00","period_end":"2026-08-12T20:10:04.620037+00:00","abcp_request_url":"unknown://cp/orders"}
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-04-02T09:30:00+00:00, period_end=2026-08-12T20:10:04.620037+00:00., {"period_start":"2026-04-02T09:30:00+00:00","period_end":"2026-08-12T20:10:04.620037+00:00","abcp_request_url":"unknown://cp/orders"}
Синхронизация заказов завершена. records_loaded=2, period_start=2026-04-14T20:10:04.620037+00:00, period_end=2026-08-12T20:10:04.620037+00:00., {"records_loaded":2,"period_start":"2026-04-14T20:10:04.620037+00:00","period_end":"2026-08-12T20:10:04.620037+00:00"}
+
+
+
\ No newline at end of file
diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_trace_messages_are_duplicated_to_console-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_trace_messages_are_duplicated_to_console-1.html
new file mode 100644
index 0000000..d3a55d4
--- /dev/null
+++ b/tests/artifacts/trace_logs/tests-test_app.py-test_trace_messages_are_duplicated_to_console-1.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Trace 2ca6de8b-8dbe-4ccd-b5a5-491739119228
+
+
+
+
+
parent_id:
child_ids:
==============================
order-sync
Подготовлен запрос синхронизации заказов ABCP. period_start=2026-06-30T10:00:00+00:00, period_end=2026-06-30T11:00:00+00:00., {"period_start":"2026-06-30T10:00:00+00:00","period_end":"2026-06-30T11:00:00+00:00"}
+
+
+
\ No newline at end of file
diff --git a/tests/artifacts/trace_logs/tests-test_app.py-test_worker_unhandled_exception_is_logged_and_traced-1.html b/tests/artifacts/trace_logs/tests-test_app.py-test_worker_unhandled_exception_is_logged_and_traced-1.html
new file mode 100644
index 0000000..96ec442
--- /dev/null
+++ b/tests/artifacts/trace_logs/tests-test_app.py-test_worker_unhandled_exception_is_logged_and_traced-1.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Trace e4cae497-d728-498f-8216-651a44e59992
+
+
+
+
+
parent_id:
child_ids:
==============================
worker-loop
Необработанное исключение воркера. worker_name=job-processing, error=RuntimeError('boom')., {"worker_name":"job-processing","error":"boom","exception_class":"RuntimeError"}
+
+
+
\ No newline at end of file
diff --git a/tests/test_trace_endpoint.py b/tests/test_trace_endpoint.py
index 79c0d0e..b412933 100644
--- a/tests/test_trace_endpoint.py
+++ b/tests/test_trace_endpoint.py
@@ -80,8 +80,8 @@ def test_trace_endpoint_returns_html_by_default() -> None:
levels=("ERROR", "WARNING", "INFO"),
include_attrs_json=False,
response_format="html",
- ancestor_depth=None,
- view="tree",
+ ancestor_depth=0,
+ view="single",
),
)
]
@@ -333,13 +333,13 @@ def test_trace_endpoint_returns_html_page_with_related_links() -> None:
assert "--error: #ff817d;" in response.text
assert "--other: #ececec;" in response.text
assert 'font: 13px/1.1 "SFMono-Regular", monospace;' in response.text
- assert '' in response.text
- assert '' in response.text
+ assert '' in response.text
+ assert '' in response.text
assert 'child_ids:
' in response.text
- assert '' in response.text
- assert '' in response.text
+ assert '' in response.text
+ assert '' in response.text
assert '==============================
' in response.text
- assert 'trace context: order_confirmation,
trace-1 ' in response.text
+ assert 'trace context: order_confirmation,
trace-1 ' in response.text
assert 'load_stocks
' in response.text
assert 'filter_stocks
' in response.text
assert "loaded prices" in response.text
@@ -450,43 +450,17 @@ def test_trace_endpoint_preserves_ancestor_depth_in_html_links() -> None:
assert "parent warning" in response.text
-def test_trace_endpoint_returns_full_tree_by_default() -> None:
+def test_trace_endpoint_returns_single_view_by_default() -> None:
async def trace_provider(trace_id: str, request: TraceQueryRequest) -> TraceLogView:
assert trace_id == "child-1"
- assert request.view == "tree"
- assert request.ancestor_depth is None
+ assert request.view == "single"
+ assert request.ancestor_depth == 0
return TraceLogView(
- trace_id="root-1",
- parent_id=None,
- alias="root",
- child_ids=("parent-1",),
- records=(_trace_record(row_id=1, level="INFO", message="root message"),),
- descendants=(
- TraceLogView(
- trace_id="parent-1",
- parent_id="root-1",
- alias="parent",
- child_ids=("child-1", "sibling-1"),
- records=(_trace_record(row_id=2, level="WARNING", message="parent message"),),
- descendants=(
- TraceLogView(
- trace_id="child-1",
- parent_id="parent-1",
- alias="child",
- child_ids=(),
- records=(_trace_record(row_id=3, level="ERROR", message="child message"),),
- ),
- TraceLogView(
- trace_id="sibling-1",
- parent_id="parent-1",
- alias="sibling",
- child_ids=(),
- records=(_trace_record(row_id=4, level="INFO", message="sibling message"),),
- ),
- ),
- ),
- ),
- requested_trace_id="child-1",
+ trace_id="child-1",
+ parent_id="parent-1",
+ alias="child",
+ child_ids=("leaf-1",),
+ records=(_trace_record(row_id=3, level="ERROR", message="child message"),),
)
client = _build_client(trace_provider)
@@ -496,8 +470,18 @@ def test_trace_endpoint_returns_full_tree_by_default() -> None:
client.close()
assert response.status_code == 200
- assert "requested_trace_id: child-1" in response.text
- assert response.text.index("root message") < response.text.index("parent message") < response.text.index("child message") < response.text.index("sibling message")
+ assert response.text == (
+ "trace_id: child-1\n"
+ "parent_id: parent-1\n"
+ "child_ids:\n"
+ " - leaf-1\n"
+ "\n"
+ "==============================\n"
+ "trace context: child, child-1\n"
+ "\n"
+ "step: process\n"
+ "child message"
+ )
def test_trace_endpoint_supports_single_view_opt_in() -> None: