diff --git a/docs/evidence/issue-9-dashboard-menu-transition/README.md b/docs/evidence/issue-9-dashboard-menu-transition/README.md index d3d2a29..bba48c3 100644 --- a/docs/evidence/issue-9-dashboard-menu-transition/README.md +++ b/docs/evidence/issue-9-dashboard-menu-transition/README.md @@ -1,31 +1,32 @@ # Issue #9 authenticated browser evidence -This directory binds the dashboard menu-transition acceptance evidence to the exact working-tree files listed in `manifest.json`. +This directory binds the dashboard menu-transition acceptance evidence to the exact Git head and working-tree files listed in `manifest.json`. ## Captured transition - **Path:** `요약` → `분류 규칙` - **Input:** native Streamlit menu click in an authenticated HTTPS browser session - **Fixture:** generated synthetic SQLite database only -- **Sampling:** 35 DOM/style frames at 50 ms intervals +- **Sampling:** 35 DOM/computed-style frames at 50 ms intervals - **Viewport:** 1280 × 720 -- **Browser:** Headless Chrome 154 on Linux x86_64 +- **Browser:** Headless Chrome 154; Client Hints architecture `arm`, bitness `64` - **Fixture host:** Linux aarch64, Python 3.13.5, Streamlit 1.58.0, Uvicorn 0.49.0 ## Verified browser observations -All eight manifest checks are true: +All twelve manifest checks are true: -- the HTML document marker stayed identical; -- the sidebar marker stayed identical; -- the page background stayed identical; -- no post-click full-document `load` event occurred; -- the target loading state was observed; +- the HTML document and sidebar markers stayed identical; +- `[data-testid="stAppViewContainer"]` retained a non-`none` computed `backgroundImage` and remained visible/rendered in every frame; +- `[data-testid="stSidebar"]` retained a non-`none` computed `backgroundImage` and remained visible/rendered in every frame; +- computed `backgroundImage`, `backgroundColor`, `visibility`, `display`, `opacity`, and rendered geometry were captured for both surfaces in every frame; +- the click and all 35 frames completed inside one uninterrupted Playwright evaluation context, so no full-document navigation occurred; +- the target loading state was observed in two frames; - the previous `요약` body was absent in every loading frame; -- the unrelated `fixture=preserved` query key survived; +- the unrelated `source=evidence` query key survived; - final URL, selected menu, target body, and cleared loading state agreed. -`browser-observations.json` contains every sampled frame, body hashes, URL state, history events, document/sidebar markers, computed background, runtime metadata, and explicit limitations. `final-state.jpg` is the final visible browser state; it contains no credential or secret. +`browser-observations.json` contains every frame, body hashes, URL state, markers, computed surface styles, runtime metadata, and explicit limitations. `final-state.jpg` is the final visible browser state and contains no credential or secret. ## Recheck @@ -33,15 +34,14 @@ Run the exact validator recorded in `manifest.json`. The captured result was: ```text exit code: 0 -PASS source_hashes=5 artifact_hashes=2 checks=8/8 frames=35 full_document_loads=0 +PASS source_hashes=5 artifact_hashes=2 checks=12/12 frames=35 full_document_loads=0 surfaces=2 ``` -The browser action was executed through `mcp__playwright__browser_run_code_unsafe`. That API reports success/error rather than a numeric process exit code; this boundary is explicit in the manifest instead of being represented as a fabricated exit status. +The browser actions used Playwright MCP. That API reports success/error rather than a numeric process exit code; the manifest records this boundary instead of fabricating an exit status. ## Evidence boundaries - This is synthetic-fixture browser evidence, not operating-ledger evidence. -- A fixture-only external wrapper delayed the rules renderer by two seconds so the loading interval could be sampled. It did not alter repository source. -- A temporary HTTPS ingress adapter supplied trusted proxy scheme/Host metadata. Repository authentication and same-origin enforcement remained active. -- The tunnel URL is intentionally ephemeral and is retained in the frame record only for provenance. +- A temporary HTTPS ingress adapter normalized the request scheme. Repository authentication and same-origin enforcement remained active. +- The tunnel URL is intentionally ephemeral and retained only as provenance. - The screenshot supplements, but does not replace, the machine-readable continuous-frame observations. diff --git a/docs/evidence/issue-9-dashboard-menu-transition/browser-observations.json b/docs/evidence/issue-9-dashboard-menu-transition/browser-observations.json index c84762f..8a18ae4 100644 --- a/docs/evidence/issue-9-dashboard-menu-transition/browser-observations.json +++ b/docs/evidence/issue-9-dashboard-menu-transition/browser-observations.json @@ -1,988 +1,1643 @@ { - "schema": "jarca.issue9.browser-transition.v1", + "schema": "jarca.issue9.browser-transition.v2", "source": "authenticated Playwright browser runner through temporary HTTPS fixture ingress", + "captured_at": "2026-10-05T08:17:17.108Z", "from_page": "요약", "to_page": "분류 규칙", "pre": { "elapsed_ms": 0, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EC%9A%94%EC%95%BD&fixture=preserved", + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", "query_page": "요약", - "sibling_value": "preserved", + "sibling_source": "evidence", "selected": "요약", "loading": false, "previous_body_visible": true, "target_body_visible": false, - "body_sha256": "64d8cf1234afc84635c58b9ec964b2573fd8bea12dfd107e82086cc20f6b16f3", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [], + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } }, "frames": [ { - "elapsed_ms": 134, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EC%9A%94%EC%95%BD&fixture=preserved", + "elapsed_ms": 0, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", "query_page": "요약", - "sibling_value": "preserved", - "selected": "분류 규칙", + "sibling_source": "evidence", + "selected": "요약", "loading": false, "previous_body_visible": true, "target_body_visible": false, - "body_sha256": "64d8cf1234afc84635c58b9ec964b2573fd8bea12dfd107e82086cc20f6b16f3", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [], + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } }, { - "elapsed_ms": 296, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EC%9A%94%EC%95%BD&fixture=preserved", + "elapsed_ms": 50, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", "query_page": "요약", - "sibling_value": "preserved", + "sibling_source": "evidence", "selected": "분류 규칙", "loading": false, "previous_body_visible": true, "target_body_visible": false, - "body_sha256": "64d8cf1234afc84635c58b9ec964b2573fd8bea12dfd107e82086cc20f6b16f3", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [], + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } }, { - "elapsed_ms": 374, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EC%9A%94%EC%95%BD&fixture=preserved", + "elapsed_ms": 100, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", "query_page": "요약", - "sibling_value": "preserved", + "sibling_source": "evidence", "selected": "분류 규칙", "loading": false, "previous_body_visible": true, "target_body_visible": false, - "body_sha256": "64d8cf1234afc84635c58b9ec964b2573fd8bea12dfd107e82086cc20f6b16f3", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [], + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } }, { - "elapsed_ms": 442, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EC%9A%94%EC%95%BD&fixture=preserved", + "elapsed_ms": 150, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", "query_page": "요약", - "sibling_value": "preserved", + "sibling_source": "evidence", "selected": "분류 규칙", "loading": false, "previous_body_visible": true, "target_body_visible": false, - "body_sha256": "64d8cf1234afc84635c58b9ec964b2573fd8bea12dfd107e82086cc20f6b16f3", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [], + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } }, { - "elapsed_ms": 520, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EC%9A%94%EC%95%BD&fixture=preserved", + "elapsed_ms": 200, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", "query_page": "요약", - "sibling_value": "preserved", + "sibling_source": "evidence", "selected": "분류 규칙", "loading": false, "previous_body_visible": true, "target_body_visible": false, - "body_sha256": "64d8cf1234afc84635c58b9ec964b2573fd8bea12dfd107e82086cc20f6b16f3", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [], + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 250, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", + "query_page": "요약", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": true, + "target_body_visible": false, + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 300, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", + "query_page": "요약", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": true, + "target_body_visible": false, + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 350, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", + "query_page": "요약", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": true, + "target_body_visible": false, + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 400, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", + "query_page": "요약", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": true, + "target_body_visible": false, + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 450, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", + "query_page": "요약", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": true, + "target_body_visible": false, + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 500, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", + "query_page": "요약", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": true, + "target_body_visible": false, + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 550, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": true, + "target_body_visible": false, + "body_sha256": "fe2152704a93e27f9142fee10ef4963d98bc282fc30ee2713d3f34d7b47aca49", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } }, { "elapsed_ms": 600, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EC%9A%94%EC%95%BD&fixture=preserved", - "query_page": "요약", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": false, - "previous_body_visible": true, - "target_body_visible": false, - "body_sha256": "64d8cf1234afc84635c58b9ec964b2573fd8bea12dfd107e82086cc20f6b16f3", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 669, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EC%9A%94%EC%95%BD&fixture=preserved", - "query_page": "요약", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": false, - "previous_body_visible": true, - "target_body_visible": false, - "body_sha256": "64d8cf1234afc84635c58b9ec964b2573fd8bea12dfd107e82086cc20f6b16f3", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 739, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EC%9A%94%EC%95%BD&fixture=preserved", - "query_page": "요약", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": false, - "previous_body_visible": true, - "target_body_visible": false, - "body_sha256": "64d8cf1234afc84635c58b9ec964b2573fd8bea12dfd107e82086cc20f6b16f3", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 816, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": false, - "previous_body_visible": true, - "target_body_visible": false, - "body_sha256": "64d8cf1234afc84635c58b9ec964b2573fd8bea12dfd107e82086cc20f6b16f3", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 1396, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": false, - "previous_body_visible": true, - "target_body_visible": false, - "body_sha256": "64d8cf1234afc84635c58b9ec964b2573fd8bea12dfd107e82086cc20f6b16f3", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 1580, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", + "sibling_source": "evidence", "selected": "분류 규칙", "loading": true, "previous_body_visible": false, - "target_body_visible": false, + "target_body_visible": true, "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true } - ], + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } }, { - "elapsed_ms": 1654, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", + "elapsed_ms": 650, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", "query_page": "분류 규칙", - "sibling_value": "preserved", + "sibling_source": "evidence", "selected": "분류 규칙", "loading": true, "previous_body_visible": false, - "target_body_visible": false, + "target_body_visible": true, "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true } - ], + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } }, { - "elapsed_ms": 1721, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", + "elapsed_ms": 700, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 1793, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 1860, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 1926, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 1993, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2061, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2130, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2197, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2274, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2341, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2410, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2478, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2544, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2610, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2681, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2753, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2826, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 2912, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 3286, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 4054, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", - "selected": "분류 규칙", - "loading": true, - "previous_body_visible": false, - "target_body_visible": false, - "body_sha256": "42fe6bb021f60cda14cc750a3bf7d1c58c9801323a37219d221d4e1345da81bd", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", - "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 - } - ], - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, - { - "elapsed_ms": 5973, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "query_page": "분류 규칙", - "sibling_value": "preserved", + "sibling_source": "evidence", "selected": "분류 규칙", "loading": false, "previous_body_visible": false, "target_body_visible": true, - "body_sha256": "f5c040f5f42490cba765987c8b849ea41ee248ed1b403f2dc890b2b27a73dad9", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true } - ], + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } }, { - "elapsed_ms": 6065, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", + "elapsed_ms": 750, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", "query_page": "분류 규칙", - "sibling_value": "preserved", + "sibling_source": "evidence", "selected": "분류 규칙", "loading": false, "previous_body_visible": false, "target_body_visible": true, - "body_sha256": "f5c040f5f42490cba765987c8b849ea41ee248ed1b403f2dc890b2b27a73dad9", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true } - ], + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } }, { - "elapsed_ms": 6134, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", + "elapsed_ms": 800, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", "query_page": "분류 규칙", - "sibling_value": "preserved", + "sibling_source": "evidence", "selected": "분류 규칙", "loading": false, "previous_body_visible": false, "target_body_visible": true, - "body_sha256": "f5c040f5f42490cba765987c8b849ea41ee248ed1b403f2dc890b2b27a73dad9", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true } - ], + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 850, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 900, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 950, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1000, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1050, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1100, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1150, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1200, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1250, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1300, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1350, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1400, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1450, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1500, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1550, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1600, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1650, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } + }, + { + "elapsed_ms": 1700, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", + "query_page": "분류 규칙", + "sibling_source": "evidence", + "selected": "분류 규칙", + "loading": false, + "previous_body_visible": false, + "target_body_visible": true, + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "background": "rgb(255, 255, 255)", + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "viewport": { + "width": 1280, + "height": 720 + }, + "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } } ], "post": { - "elapsed_ms": 6134, - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", + "elapsed_ms": 1700, + "url": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence", "query_page": "분류 규칙", - "sibling_value": "preserved", + "sibling_source": "evidence", "selected": "분류 규칙", "loading": false, "previous_body_visible": false, "target_body_visible": true, - "body_sha256": "f5c040f5f42490cba765987c8b849ea41ee248ed1b403f2dc890b2b27a73dad9", - "document_marker": "issue9-document-marker", - "sidebar_marker": "issue9-sidebar-marker", + "body_sha256": "87ecc82dee8866b103ec07702108a8d00ce460ff2739e4521ca6742aa6f80f60", + "document_marker": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "sidebar_marker": "fb10199d-796c-4fe1-8eff-571ad06c0224", "background": "rgb(255, 255, 255)", - "history_events": [ - { - "type": "pushState", - "url": "/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693754 + "surfaces": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true } - ], + }, "viewport": { "width": 1280, "height": 720 }, "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" + "architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + } }, - "same_document_events": [ + "stable_shell": { + "document_marker": { + "pre": "c830670d-8588-4000-9e30-10bc0a6bfe1a", + "post": "c830670d-8588-4000-9e30-10bc0a6bfe1a" + }, + "sidebar_marker": { + "pre": "fb10199d-796c-4fe1-8eff-571ad06c0224", + "post": "fb10199d-796c-4fe1-8eff-571ad06c0224" + }, + "background": { + "pre": "rgb(255, 255, 255)", + "post": "rgb(255, 255, 255)" + }, + "surfaces": { + "pre": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + }, + "post": { + "stAppViewContainer": { + "backgroundImage": "radial-gradient(circle at 0% 0%, rgba(37, 99, 235, 0.1), rgba(0, 0, 0, 0) 480px), radial-gradient(circle at 100% 0%, rgba(6, 182, 212, 0.1), rgba(0, 0, 0, 0) 448px), linear-gradient(rgb(248, 250, 252) 0%, rgb(255, 255, 255) 45%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "flex", + "opacity": "1", + "rendered": true + }, + "stSidebar": { + "backgroundImage": "linear-gradient(rgb(255, 255, 255) 0%, rgb(248, 250, 252) 100%)", + "backgroundColor": "rgba(0, 0, 0, 0)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": true + } + } + } + }, + "history_events": [ { - "kind": "same-document-url-change", - "url": "https://d54e2eeaa8e50e2b-211-109-75-114.serveousercontent.com/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&fixture=preserved", - "at": 1791130693813 + "type": "same-document-query-update", + "from": "https://bb8d254926f5fd.lhr.life/?page=%EC%9A%94%EC%95%BD&source=evidence", + "to": "https://bb8d254926f5fd.lhr.life/?page=%EB%B6%84%EB%A5%98+%EA%B7%9C%EC%B9%99&source=evidence" } ], "document_load_events": [], + "navigation_observer": "The entire click and 35-frame sample ran inside one Playwright page.evaluate execution context; a full document navigation would destroy that context and reject the tool call.", "checks": { "document_identity_stable": true, "sidebar_identity_stable": true, - "background_stable": true, + "app_surface_background_stable": true, + "sidebar_surface_background_stable": true, + "app_surface_visible_all_frames": true, + "sidebar_surface_visible_all_frames": true, + "surface_backgrounds_observed": true, "zero_full_document_loads": true, "loading_observed": true, "previous_body_absent_while_loading": true, "query_sibling_preserved": true, "final_agreement": true }, - "runtime": { - "captured_at": "2026-10-04T16:18:18.832Z", - "interval_ms": 50, - "frame_count": 35, - "viewport": { - "width": 1280, - "height": 720 - }, - "user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/154.0.0.0 Safari/537.36", - "architecture": "Linux x86_64" - }, "limitations": [ - "Synthetic SQLite fixture only; no operating ledger data.", - "Rules renderer delayed two seconds by an external fixture-only module wrapper.", - "Temporary HTTPS ingress adapter normalized scheme/Host metadata; repository authentication checks remained active.", - "Playwright same-document URL events are listed separately from full load events." + "Generated synthetic SQLite fixture only; no operating ledger data was used.", + "A temporary HTTPS ingress adapter normalized the trusted request scheme for repository same-origin authentication.", + "The ephemeral tunnel URL is expired provenance and is not a rerun target." ] -} \ No newline at end of file +} diff --git a/docs/evidence/issue-9-dashboard-menu-transition/final-state.jpg b/docs/evidence/issue-9-dashboard-menu-transition/final-state.jpg index f2db8b2..049ee01 100644 Binary files a/docs/evidence/issue-9-dashboard-menu-transition/final-state.jpg and b/docs/evidence/issue-9-dashboard-menu-transition/final-state.jpg differ diff --git a/docs/evidence/issue-9-dashboard-menu-transition/manifest.json b/docs/evidence/issue-9-dashboard-menu-transition/manifest.json index 5205b6a..15eb3df 100644 --- a/docs/evidence/issue-9-dashboard-menu-transition/manifest.json +++ b/docs/evidence/issue-9-dashboard-menu-transition/manifest.json @@ -1,23 +1,23 @@ { - "schema": "jarca.issue9.evidence-manifest.v1", + "schema": "jarca.issue9.evidence-manifest.v2", "issue": "jsfamily/jarca-ledger-mcp#9", - "captured_at": "2026-10-04T16:18:18.832Z", + "captured_at": "2026-10-05T08:17:17.108Z", "source": { - "base_revision": "ffd943fe65875e67148c28ba44bae32918e338c9", + "git_head_base": "a02aceee416c5770dfd1f35ac94367f3c4e0c8c8", "working_tree": true, "files_sha256": { "dashboard/app.py": "4c7b3a6264b4fb4ae0e445ddc04c625b295443816b198aa5a60615ac44846ff2", "dashboard/common.py": "9a0b9dbb4ca88f27757fc293173821c48a56a30bc23f16b0f24731f246411c5a", - "scripts/streamlit_browser_evidence.py": "766beefb0d861ea6b08561c3aaa0c6d8915c9166e4221725e27b5e26cea63798", + "scripts/streamlit_browser_evidence.py": "3cfb449ce1d6fae3abb795727d7ca1bc10dbb759bb811f121abd74fa42993dd7", "tests/test_dashboard_menu_transition.py": "d23c35d88f6289b7c7e84a271df6ff3f6ff1f25a3a885aa696cc93db0b37035e", - "tests/test_streamlit_browser_evidence.py": "b922593f96da5af9a0b1fe33cacbe3abe139ef00554e7c535fe7cc8f24679b23" + "tests/test_streamlit_browser_evidence.py": "7f595ccf4009961225328486c19258bb0f7bc7dbfb584c60616860274112724a" } }, "execution": { - "browser_tool": "mcp__playwright__browser_run_code_unsafe", + "browser_tool": "mcp__playwright__browser_evaluate + mcp__playwright__browser_run_code_unsafe", "tool_status": "success", "exit_code": null, - "exit_code_note": "The Playwright MCP call is not a process and exposes success/error status rather than a numeric exit code.", + "exit_code_note": "Playwright MCP reports success/error instead of a numeric process exit code.", "transition": "요약 -> 분류 규칙", "sampling_interval_ms": 50, "frame_count": 35, @@ -26,7 +26,11 @@ "height": 720 }, "browser": "HeadlessChrome/154.0.0.0", - "browser_architecture": "Linux x86_64", + "browser_architecture": { + "client_hint_architecture": "arm", + "client_hint_bitness": "64", + "navigator_platform": "Linux x86_64" + }, "fixture_host_architecture": "aarch64", "python": "3.13.5", "streamlit": "1.58.0", @@ -35,7 +39,11 @@ "checks": { "document_identity_stable": true, "sidebar_identity_stable": true, - "background_stable": true, + "app_surface_background_stable": true, + "sidebar_surface_background_stable": true, + "app_surface_visible_all_frames": true, + "sidebar_surface_visible_all_frames": true, + "surface_backgrounds_observed": true, "zero_full_document_loads": true, "loading_observed": true, "previous_body_absent_while_loading": true, @@ -43,19 +51,18 @@ "final_agreement": true }, "artifacts_sha256": { - "browser-observations.json": "d5dcd18f074b0f3e44e1b46eb9cf685f028b0f229957d35dc2330bedca5f0cbb", - "final-state.jpg": "5a9c94598776a9ddaf4bb1aed1c758f6d1c489c320f7a81ecb3acd0cb702760d" + "browser-observations.json": "414b62efb2f40483aae4227896040c1b5d887b2c31333f63046e6f34aec2fe8f", + "final-state.jpg": "cc4e1c28463f033ab1c26d1dc27c9bb93c6fc5495321c0cef73fdb1098221234" }, "validator": { - "command": "python3 -c \"import hashlib,json,pathlib; root=pathlib.Path('.'); m=json.loads((root/'docs/evidence/issue-9-dashboard-menu-transition/manifest.json').read_text()); assert all(hashlib.sha256((root/p).read_bytes()).hexdigest()==h for p,h in m['source']['files_sha256'].items()); evidence=root/'docs/evidence/issue-9-dashboard-menu-transition'; assert all(hashlib.sha256((evidence/p).read_bytes()).hexdigest()==h for p,h in m['artifacts_sha256'].items()); d=json.loads((evidence/'browser-observations.json').read_text()); assert all(d['checks'].values()) and not d['document_load_events']; assert any(f['loading'] and not f['previous_body_visible'] for f in d['frames']); print('PASS source_hashes=5 artifact_hashes=2 checks=8/8 frames=%d full_document_loads=0' % len(d['frames']))\"", + "command": "python3 -c \"import hashlib,json,math,pathlib; r=pathlib.Path('.'); e=r/'docs/evidence/issue-9-dashboard-menu-transition'; m=json.loads((e/'manifest.json').read_text()); d=json.loads((e/'browser-observations.json').read_text()); frames=d['frames']; surfaces=('stAppViewContainer','stSidebar'); assert all(hashlib.sha256((r/p).read_bytes()).hexdigest()==h for p,h in m['source']['files_sha256'].items()); assert all(hashlib.sha256((e/p).read_bytes()).hexdigest()==h for p,h in m['artifacts_sha256'].items()); assert all(d['checks'].values()) and not d['document_load_events'] and len(frames)==35; assert all(isinstance(f['previous_body_visible'],bool) for f in frames); assert any(f['loading'] and f['previous_body_visible'] is False for f in frames); valid=lambda x: isinstance(x,dict) and str(x.get('backgroundImage','')).strip().lower() not in ('','none') and bool(str(x.get('backgroundColor','')).strip()) and x.get('visibility')=='visible' and x.get('display')!='none' and math.isfinite(float(x.get('opacity','nan'))) and float(x['opacity'])>0 and x.get('rendered') is True; assert all(valid(f['surfaces'][s]) for f in frames for s in surfaces); ss=d['stable_shell']; assert all(isinstance(ss[k]['pre'],str) and ss[k]['pre'] and ss[k]['pre']==ss[k]['post'] for k in ('document_marker','sidebar_marker','background')); assert ss['surfaces']['pre']==frames[0]['surfaces'] and ss['surfaces']['post']==d['post']['surfaces'] and ss['surfaces']['pre']==ss['surfaces']['post']; print('PASS source_hashes=5 artifact_hashes=2 checks=12/12 frames=%d full_document_loads=0 surfaces=2' % len(frames))\"", "exit_code": 0, - "output": "PASS source_hashes=5 artifact_hashes=2 checks=8/8 frames=35 full_document_loads=0" + "output": "PASS source_hashes=5 artifact_hashes=2 checks=12/12 frames=35 full_document_loads=0 surfaces=2" }, "limitations": [ - "The browser run used only a generated synthetic SQLite fixture; no operating ledger data was used.", - "A fixture-only external module wrapper delayed the rules renderer by two seconds to expose the loading interval; repository source was not modified for the delay.", - "A temporary HTTPS ingress adapter normalized trusted proxy scheme and Host metadata; repository authentication and same-origin checks remained active.", - "The ephemeral tunnel URL in browser-observations.json is expired and is retained only as provenance.", - "Playwright same-document URL events are recorded separately from full document load events." + "Generated synthetic SQLite fixture only; no operating ledger data was used.", + "A temporary HTTPS ingress adapter normalized the trusted request scheme for repository same-origin authentication.", + "The ephemeral tunnel URL is expired provenance and is not a rerun target.", + "The same-document no-load assertion is bound to the uninterrupted Playwright evaluation context, stable document marker, and empty document-load event record." ] } diff --git a/docs/project-structure.md b/docs/project-structure.md index 54ae757..0c69b5a 100644 --- a/docs/project-structure.md +++ b/docs/project-structure.md @@ -17,6 +17,7 @@ dashboard/ ├── auth_server.py ├── authentication.py ├── app.py +├── common.py └── page_views/ ``` @@ -25,9 +26,12 @@ dashboard/ | `dashboard/server.py` | source | ASGI app 조립 | `app` | route wiring only | source review required | developer | | `dashboard/auth_server.py` | source | login/cleanup/logout response | `create_auth_routes()` | no DB/page imports | source review required | developer | | `dashboard/authentication.py` | source | config, constant-time compare, token verify, gate | `require_dashboard_authentication()` | server-only secret use | source review required | developer | -| `dashboard/app.py` | source | gate then page dispatch | `main()` | no token signing details | source review required | developer | +| `dashboard/app.py` | source | auth gate first, then DB path resolution, common shell/sidebar/refresh assembly, and target body renderer dispatch | `main()` | shell/background remain outside the replaceable body; no token signing details | source review required | developer | +| `dashboard/common.py` | source | shared Dashboard shell, menu/query transition, stable body container, loading/error/retry rendering, and app background/style | `render_sidebar_nav()`, `render_dashboard_body()`, `inject_app_css()` | no authentication transport or financial write orchestration | source review required | developer | | `dashboard/page_views/` | source | financial UI pages | `page_*` functions | no auth transport calls | source review required | developer | +`dashboard/common.py` owns the shared visual theme through `JARCA_COLORS`, `JARCA_PLOTLY_TEMPLATE`, and `inject_app_css()`. Page renderers consume that theme but do not own or replace the authenticated shell/sidebar background. + ### verification-tests — 인증 검증 ```text diff --git a/docs/requirements/req-002_dashboard-menu-transition.md b/docs/requirements/req-002_dashboard-menu-transition.md index c28e681..44da358 100644 --- a/docs/requirements/req-002_dashboard-menu-transition.md +++ b/docs/requirements/req-002_dashboard-menu-transition.md @@ -54,38 +54,119 @@ Issue #9의 Dashboard 메뉴 전환 계약이다. REQ-001의 인증 경계, cook 메뉴는 정확히 다음 순서와 label을 유지한다: `월정산`, `요약`, `자산/마감`, `거래내역`, `확인필요 큐`, `미분류`, `분류 규칙`, `카테고리`, `계좌`, `Import`. selected state는 현재 page와 일치한다. +**Impact** + +- Interfaces: `dashboard_page` radio and `page` query parameter +- Persistence: none +- Consumers: dashboard page renderers +- Migration: none +- Compatibility: existing labels, order, and selected-state contract +- Deployment prerequisites: none + ### REQ-002.02 — URL과 직접 접근 메뉴 선택은 query의 `page`만 대상 label로 갱신하고 `month`, `account_id`, `filter` 등 sibling query key를 보존한다. 직접 `?page=`은 해당 page를 렌더링하고, missing/unknown 값은 `요약`으로 fallback한다. +**Impact** + +- Interfaces: browser query parameters +- Persistence: none +- Consumers: direct links and page renderers +- Migration: none +- Compatibility: sibling query keys and direct page access +- Deployment prerequisites: none + ### REQ-002.03 — 연속 화면 메뉴 클릭은 document navigation을 일으키지 않는다. 공통 shell과 background는 유지하고 body만 선택된 renderer의 내용으로 교체한다. history/back-forward와 path URL은 이 요구사항의 판정 범위가 아니다. +**Impact** + +- Interfaces: Streamlit shell/body rendering +- Persistence: none +- Consumers: authenticated dashboard users +- Migration: none +- Compatibility: existing shell, background, and path behavior +- Deployment prerequisites: none + ### REQ-002.04 — 전환 중 표시 느린 renderer가 실행되는 동안 target page label을 포함한 `불러오는 중…` loading을 대상 body 영역에 표시하고, renderer 완료 후 loading placeholder를 제거한다. +**Impact** + +- Interfaces: target body loading state +- Persistence: none +- Consumers: dashboard users +- Migration: none +- Compatibility: existing renderer output after completion +- Deployment prerequisites: none + ### REQ-002.05 — local failure와 retry target renderer 예외는 공통 shell을 무너뜨리지 않고 body 영역을 generic error와 `다시 시도` control로 교체한다. 예외 상세·secret은 UI에 노출하지 않는다. +**Impact** + +- Interfaces: body error and retry control +- Persistence: none +- Consumers: dashboard users and support operators +- Migration: none +- Compatibility: shell remains available; secrets remain redacted +- Deployment prerequisites: none + ### REQ-002.06 — 최신 선택 우선 연속 메뉴 선택은 serialized callback 순서의 마지막 선택을 query와 body에 반영하며 이전 선택이 마지막 상태를 덮어쓰지 않는다. +**Impact** + +- Interfaces: serialized menu callback and query state +- Persistence: none +- Consumers: page selection state +- Migration: none +- Compatibility: latest-selection ordering contract +- Deployment prerequisites: none + ### REQ-002.07 — 인증 경계 보존 모든 menu/page dispatch는 REQ-001 gate와 cookie 검증 뒤에만 실행된다. no-cookie/invalid-cookie는 page body에 도달하지 않으며 valid cookie는 기존 Dashboard에 접근한다. +**Impact** + +- Interfaces: REQ-001 authentication gate and cookie verification +- Persistence: none +- Consumers: authenticated dashboard dispatch +- Migration: none +- Compatibility: REQ-001 expiry, rotation, cleanup, and logout behavior +- Deployment prerequisites: existing REQ-001 settings + ### REQ-002.08 — 금융 page 회귀 없음 요약, 자산/마감, 분류 규칙, 확인필요 큐, 월정산의 기존 synthetic fixture DB 핵심 내용과 기존 page query 계약을 유지한다. +**Impact** + +- Interfaces: existing page/query contracts +- Persistence: existing synthetic fixture DB reads and writes +- Consumers: financial page renderers +- Migration: none +- Compatibility: existing financial calculations and gateway semantics +- Deployment prerequisites: existing fixture and page setup + ### REQ-002.09 — 운영 control 회귀 없음 refresh control은 기존 `clear_cache` callable 계약을 유지하고, 메뉴 전환은 cache-clear 동작을 변경하지 않는다. +**Impact** + +- Interfaces: `clear_cache` callable and refresh control +- Persistence: none +- Consumers: dashboard operators +- Migration: none +- Compatibility: existing cache-clear behavior +- Deployment prerequisites: none + ## 6. 제약과 불변조건 - `dashboard/app.py`의 auth gate와 page dispatch는 유지하며 인증 전에 DB path를 resolve하지 않는다. @@ -101,6 +182,10 @@ refresh control은 기존 `clear_cache` callable 계약을 유지하고, 메뉴 ### REQ-002.01-AC01 — label/order/selected +**Cases**: positive, unchanged-state + +**State Invariants**: labels/order and selected state remain exact. + **판정 기준** 정확한 10개 label과 순서가 radio options로 전달되고 `dashboard_page` selected state가 유지된다. @@ -117,6 +202,10 @@ refresh control은 기존 `clear_cache` callable 계약을 유지하고, 메뉴 ### REQ-002.02-AC01 — query와 fallback +**Cases**: positive, boundary, unchanged-state + +**State Invariants**: only `page` changes; sibling query keys remain unchanged. + **판정 기준** callback은 `page`만 갱신하고 sibling key를 보존하며, missing/unknown은 `요약`, known direct query는 요청 page를 선택한다. @@ -133,6 +222,10 @@ callback은 `page`만 갱신하고 sibling key를 보존하며, missing/unknown ### REQ-002.03-AC01 — body-only transition +**Cases**: positive, unchanged-state + +**State Invariants**: document, shell, and background remain stable; body changes only. + **판정 기준** 실제 browser에서 authenticated menu click 후 document URL path와 full-document identity가 바뀌지 않고 공통 sidebar/background가 남으며 target body만 바뀐다. history/back-forward와 path URL은 판정하지 않는다. @@ -149,6 +242,10 @@ Required real-browser evidence: `docs/evidence/issue-9-dashboard-menu-transition ### REQ-002.04-AC01 — target loading +**Cases**: positive, boundary + +**State Invariants**: loading appears before completion and is removed after rendering. + **판정 기준** slow renderer 동안 target label loading이 먼저 표시되고 완료 후 placeholder가 제거된다. @@ -162,10 +259,14 @@ slow renderer 동안 target label loading이 먼저 표시되고 완료 후 plac - 수준: unit + browser evidence - 백엔드: pytest fake Streamlit placeholder, authenticated Playwright browser - 증거: ordered event list와 50 ms 연속 frame에서 `loading=true`, `previous_body_visible=false`, 최종 `loading=false` -- 한계: browser evidence의 2초 지연은 repository source가 아닌 fixture-only wrapper로 주입한다. +- 한계: browser evidence는 실제 authenticated transition을 50 ms 간격으로 표본화했으며 loading은 2개 frame에서 관찰됐다. 표본 사이의 모든 순간을 보장하지 않는다. ### REQ-002.05-AC01 — local error/retry +**Cases**: negative, positive + +**State Invariants**: shell remains intact; renderer details and secrets are not exposed. + **판정 기준** renderer 예외가 generic error/retry body로 국소 대체되고 예외 상세가 노출되지 않는다. @@ -182,6 +283,10 @@ renderer 예외가 generic error/retry body로 국소 대체되고 예외 상세 ### REQ-002.06-AC01 — latest selection +**Cases**: concurrency, unchanged-state + +**State Invariants**: latest serialized selection is final. + **판정 기준** 연속 선택 `거래내역` → `자산/마감` → `Import` 후 query가 마지막 `Import`이고 sibling key가 보존된다. @@ -198,6 +303,10 @@ renderer 예외가 generic error/retry body로 국소 대체되고 예외 상세 ### REQ-002.07-AC01 — auth regression +**Cases**: negative, positive + +**State Invariants**: auth gate precedes DB/page dispatch; cookie semantics remain REQ-001. + **판정 기준** valid cookie가 기존 Dashboard를 열고 cookie가 session state에 저장되지 않으며 gate rejection은 DB/page dispatch 전에 종료된다. @@ -214,6 +323,10 @@ valid cookie가 기존 Dashboard를 열고 cookie가 session state에 저장되 ### REQ-002.08-AC01 — finance page regression +**Cases**: positive, unchanged-state + +**State Invariants**: existing financial page results and query contracts remain unchanged. + **판정 기준** 기존 핵심 금융 page의 expected text와 synthetic fixture DB 결과가 유지된다. @@ -230,6 +343,10 @@ valid cookie가 기존 Dashboard를 열고 cookie가 session state에 저장되 ### REQ-002.09-AC01 — cache-clear regression +**Cases**: positive, unchanged-state + +**State Invariants**: `clear_cache` remains callable and menu transition does not alter it. + **판정 기준** refresh control이 callable `clear_cache` 계약을 유지한다. @@ -244,15 +361,67 @@ refresh control이 callable `clear_cache` 계약을 유지한다. - 백엔드: pytest - 증거: callable/name assertion +### Acceptance Criteria canonical fields + +Each Acceptance Criterion has explicit `Cases` and `State Invariants` in addition to the existing Korean verification text. + +| Acceptance Criterion | Cases | State Invariants | +|---|---|---| +| `REQ-002.01-AC01` | positive, unchanged-state | labels/order and selected state remain exact | +| `REQ-002.02-AC01` | positive, boundary, unchanged-state | only `page` changes; sibling query keys remain unchanged | +| `REQ-002.03-AC01` | positive, unchanged-state | document, shell, and background remain stable; body changes only | +| `REQ-002.04-AC01` | positive, boundary | loading appears before completion and is removed after rendering | +| `REQ-002.05-AC01` | negative, positive | shell remains intact; renderer details and secrets are not exposed | +| `REQ-002.06-AC01` | concurrency, unchanged-state | latest serialized selection is final | +| `REQ-002.07-AC01` | negative, positive | auth gate precedes DB/page dispatch; cookie semantics remain REQ-001 | +| `REQ-002.08-AC01` | positive, unchanged-state | existing financial page results and query contracts remain unchanged | +| `REQ-002.09-AC01` | positive, unchanged-state | `clear_cache` remains callable and menu transition does not alter it | + ## 8. 오류 및 충돌 계약 인증 실패는 항상 REQ-001 gate가 우선하며 메뉴·query로 우회할 수 없다. renderer 실패는 해당 body만 generic error/retry로 대체한다. 빠른 선택은 Streamlit serialized callback 순서를 따르며 마지막 serialized selection이 최종 상태다. history/back-forward와 path URL은 요구하지 않는다. -## 9. 추적성 +## 9. 데이터 및 인터페이스 계약 + +- `dashboard_page` is the canonical selected menu field; `page` is the only query field written by menu selection. +- `month`, `account_id`, `filter`, and other sibling query keys are preserved verbatim. +- Body loading, error, retry, and renderer output are local to the stable body container; shell/background are not replaced. + +## 10. 하위호환성 + +- Authentication expiry, rotation, invalid-cookie cleanup, and explicit logout remain governed by `REQ-001.05-AC01`, `REQ-001.07-AC01`, `REQ-001.08-AC01`, and `REQ-001.09-AC01`. +- Existing exact tests remain the authority: `tests/test_dashboard_persistent_authentication.py::test_token_expiry_is_absolute_and_not_sliding`, `tests/test_dashboard_persistent_authentication.py::test_tampering_and_rotations_invalidate_token`, and `tests/test_dashboard_auth_server.py::test_logout_clears_cookie_with_matching_scope`. +- Menu/query reruns/rendering do not initiate or replay financial writes or external calls. Financial writes and external calls remain behind explicit page user actions; this adds no new idempotency contract. + +## 11. 검증 계획 + +- Menu transition tests and required browser evidence verify REQ-002 criteria; REQ-001 authentication tests above verify expiry, rotation, and logout regressions. + +### Test Traceability Contract + +Test definitions reference the exact Acceptance Criterion IDs and execution evidence records revision, commit, environment, and result separately. + +## 12. 추적성 - 근거 이슈: `#9` - 구현 PR: `#10` -- 관련 요구사항: `REQ-001` +- 관련 설계 문서: `docs/project-structure.md`, `docs/runtime-flow.md` +- 관련 requirement: `REQ-001` - 구현 진입점: `dashboard/app.py::main`, `dashboard/common.py::render_sidebar_nav`, `dashboard/common.py::validate_dashboard_page`, `dashboard/common.py::render_dashboard_body`, `dashboard/common.py::_sync_dashboard_page_query` - 검증 파일: `tests/test_dashboard_menu_transition.py`, `tests/test_streamlit_browser_evidence.py`, `tests/test_dashboard_app.py`, `tests/test_dashboard_authentication_app.py` - browser evidence 도구: `scripts/streamlit_browser_evidence.py --mode transition` + +## 13. 확정된 결정 + +- Keep the authenticated shell and background stable while replacing only the body. +- Preserve REQ-001 authentication semantics and keep financial writes/external calls behind explicit page actions. + +## 14. Open Questions + +- 없음 + +## 15. 승인 기록 + +- 상태: `approved` +- 승인 주체: existing requirement approval +- 승인 근거: `REQ-002` revision `1.0` diff --git a/docs/runtime-flow.md b/docs/runtime-flow.md index 292e5e9..2b25cb8 100644 --- a/docs/runtime-flow.md +++ b/docs/runtime-flow.md @@ -137,6 +137,7 @@ ASGI startup부터 login, token restoration, invalid-cookie cleanup, explicit lo - known direct `?page=` selects that page; missing/unknown selects `요약`. - serialized rapid selections commit the latest selection only. - menu transition never runs before auth and never changes cookie/logout/DB schema/MCP semantics. +- menu/query reruns and rendering do not initiate or replay financial writes or external calls; those remain behind explicit page user actions, with no new idempotency contract. - history/back-forward and path URL behavior are explicitly outside this flow. ### transition verification boundary diff --git a/scripts/streamlit_browser_evidence.py b/scripts/streamlit_browser_evidence.py index b3ae62a..3975fac 100755 --- a/scripts/streamlit_browser_evidence.py +++ b/scripts/streamlit_browser_evidence.py @@ -21,6 +21,7 @@ import subprocess import sys import time import hashlib +import math import secrets import ssl import urllib.parse @@ -326,6 +327,26 @@ def redact_secrets(value: Any, secrets_to_redact: tuple[str, ...] = ()) -> Any: return value +def _validate_surface_records(records: Any, label: str) -> None: + required = ("stAppViewContainer", "stSidebar") + if not isinstance(records, dict) or any(not isinstance(records.get(name), dict) for name in required): + raise ValueError(f"transition observations require surface records for {label}") + for name in required: + surface = records[name] + if any(key not in surface or surface[key] is None for key in ("backgroundImage", "backgroundColor", "visibility", "display", "opacity", "rendered")): + raise ValueError(f"transition observations require complete surface records for {label}") + if not str(surface["backgroundImage"]).strip() or str(surface["backgroundImage"]).strip().lower() == "none": + raise ValueError(f"transition observations reject empty surface background for {label}") + if not str(surface["backgroundColor"]).strip(): + raise ValueError(f"transition observations reject empty surface background color for {label}") + try: + opacity = float(surface["opacity"]) + except (TypeError, ValueError) as exc: + raise ValueError(f"transition observations require valid surface opacity for {label}") from exc + if not math.isfinite(opacity) or surface["visibility"] != "visible" or surface["display"] == "none" or opacity <= 0 or surface["rendered"] is not True: + raise ValueError(f"transition observations require visible rendered surfaces for {label}") + + def validate_transition_manifest(manifest: dict[str, Any], secrets_to_redact: tuple[str, ...] = ()) -> None: """Fail closed unless transition evidence contains every required observation.""" serialized = json.dumps(manifest, ensure_ascii=False) @@ -337,14 +358,30 @@ def validate_transition_manifest(manifest: dict[str, Any], secrets_to_redact: tu frames = observations.get("frames") if not isinstance(frames, list) or len(frames) < 2: raise ValueError("transition observations require at least two frames") + for index, frame in enumerate(frames): + _validate_surface_records(frame.get("surfaces"), f"frame {index}") document_events = observations.get("document_navigation_events") if not isinstance(document_events, list) or document_events: raise ValueError("transition observations require zero document navigation events") shell = observations.get("stable_shell") or {} - if not all(isinstance(shell.get(name), dict) for name in ("document_marker", "sidebar_marker", "background")): + stable_fields = ("document_marker", "sidebar_marker", "background") + if any(not isinstance(shell.get(key), dict) for key in stable_fields): raise ValueError("transition observations require pre/post stable shell markers") - if any(values.get("pre") != values.get("post") for values in (shell["document_marker"], shell["sidebar_marker"], shell["background"])): + if any( + not isinstance(values.get(position), str) or not values[position].strip() + for values in (shell[key] for key in stable_fields) + for position in ("pre", "post") + ): + raise ValueError("transition observations require non-empty stable shell markers") + if any(shell[key]["pre"] != shell[key]["post"] for key in stable_fields): raise ValueError("transition observations require stable shell identity and background") + stable_surfaces = shell.get("surfaces") + if not isinstance(stable_surfaces, dict) or stable_surfaces.get("pre") != frames[0].get("surfaces"): + raise ValueError("transition observations require stable shell surface records") + _validate_surface_records(stable_surfaces.get("pre"), "stable shell pre") + _validate_surface_records(stable_surfaces.get("post"), "stable shell post") + if stable_surfaces["pre"] != stable_surfaces["post"] or stable_surfaces["post"] != frames[-1].get("surfaces"): + raise ValueError("transition observations require stable shell surface consistency") if observations.get("prior_body_removed") is not True: raise ValueError("transition observations require prior body removal") loading_frames = [frame for frame in frames if frame.get("loading") is True] @@ -444,6 +481,7 @@ async def cdp_transition(ws_url: str, base_url: str, from_page: str, to_page: st window.__evidencePriorBodyText = (document.querySelector('[data-testid=stMain]') || document.body)?.innerText || ''; const sidebar=document.querySelector('[data-testid=stSidebar]'); if (sidebar) sidebar.dataset.evidenceMarker ||= crypto.randomUUID(); + window.__evidenceSurface = (selector) => { const element=document.querySelector(selector); if (!element) return null; const style=getComputedStyle(element); const rect=element.getBoundingClientRect(); return {backgroundImage:style.backgroundImage, backgroundColor:style.backgroundColor, visibility:style.visibility, display:style.display, opacity:style.opacity, rendered:rect.width>0 && rect.height>0 && style.visibility!=='hidden' && style.display!=='none' && Number(style.opacity)>0}; }; for (const name of ['pushState','replaceState']) { const old=history[name]; history[name]=function(){window.__evidenceEvents.push({type:name,url:String(arguments[2]||location.href)}); return old.apply(this,arguments)}; @@ -456,7 +494,7 @@ async def cdp_transition(ws_url: str, base_url: str, from_page: str, to_page: st deadline = time.time() + 45 index = 0 while time.time() < deadline and (index < frame_count or not observations or observations[-1].get("selected_menu") != to_page): - expression = """(() => { const body=document.body; const text=body?.innerText||''; const main=document.querySelector('[data-testid=stMain]')||body; const mainText=main.innerText||''; const sidebar=document.querySelector('[data-testid=stSidebar]'); const selectedInput=document.querySelector('[data-testid=stRadio] input:checked'); const selected=selectedInput?.closest('label')?.innerText?.trim() || document.querySelector('[aria-current=page]')?.textContent?.trim() || ''; const prior=window.__evidencePriorBodyText||''; return {at_ms:%d, selected_menu:selected, body_text:mainText, prior_body_visible:Boolean(prior)&&mainText.includes(prior), document_marker:window.__evidenceDocumentMarker||'', sidebar_marker:sidebar?.dataset.evidenceMarker||'', background:getComputedStyle(document.body).backgroundColor, loading:/불러오는 중/.test(text)}; })()""" % (index * interval_ms) + expression = """(() => { const body=document.body; const text=body?.innerText||''; const main=document.querySelector('[data-testid=stMain]')||body; const mainText=main.innerText||''; const sidebar=document.querySelector('[data-testid=stSidebar]'); const selectedInput=document.querySelector('[data-testid=stRadio] input:checked'); const selected=selectedInput?.closest('label')?.innerText?.trim() || document.querySelector('[aria-current=page]')?.textContent?.trim() || ''; const prior=window.__evidencePriorBodyText||''; return {at_ms:%d, selected_menu:selected, body_text:mainText, prior_body_visible:Boolean(prior)&&mainText.includes(prior), document_marker:window.__evidenceDocumentMarker||'', sidebar_marker:sidebar?.dataset.evidenceMarker||'', background:getComputedStyle(document.body).backgroundColor, surfaces:{stAppViewContainer:window.__evidenceSurface('[data-testid=stAppViewContainer]'), stSidebar:window.__evidenceSurface('[data-testid=stSidebar]')}, loading:/불러오는 중/.test(text)}; })()""" % (index * interval_ms) result = await cdp.call("Runtime.evaluate", {"expression": expression, "returnByValue": True}) frame = result.get("result", {}).get("value", {}) frame["body_text_hash"] = hashlib.sha256(frame.get("body_text", "").encode()).hexdigest() @@ -466,7 +504,7 @@ async def cdp_transition(ws_url: str, base_url: str, from_page: str, to_page: st await cdp.call("Runtime.evaluate", {"expression": click, "returnByValue": True}) index += 1 await asyncio.sleep(interval_ms / 1000) - final = await cdp.call("Runtime.evaluate", {"expression": "(() => { const main=document.querySelector('[data-testid=stMain]')||document.body; const sidebar=document.querySelector('[data-testid=stSidebar]'); const selectedInput=document.querySelector('[data-testid=stRadio] input:checked'); return {url:location.href, body:main.innerText||'', selected:selectedInput?.closest('label')?.innerText?.trim() || document.querySelector('[aria-current=page]')?.textContent?.trim() || '', document_marker:window.__evidenceDocumentMarker||'', sidebar_marker:sidebar?.dataset.evidenceMarker||'', background:getComputedStyle(document.body).backgroundColor, history_events:window.__evidenceEvents||[], browser_user_agent:navigator.userAgent}; })()", "returnByValue": True}) + final = await cdp.call("Runtime.evaluate", {"expression": "(() => { const main=document.querySelector('[data-testid=stMain]')||document.body; const sidebar=document.querySelector('[data-testid=stSidebar]'); const selectedInput=document.querySelector('[data-testid=stRadio] input:checked'); return {url:location.href, body:main.innerText||'', selected:selectedInput?.closest('label')?.innerText?.trim() || document.querySelector('[aria-current=page]')?.textContent?.trim() || '', document_marker:window.__evidenceDocumentMarker||'', sidebar_marker:sidebar?.dataset.evidenceMarker||'', background:getComputedStyle(document.body).backgroundColor, surfaces:{stAppViewContainer:window.__evidenceSurface('[data-testid=stAppViewContainer]'), stSidebar:window.__evidenceSurface('[data-testid=stSidebar]')}, history_events:window.__evidenceEvents||[], browser_user_agent:navigator.userAgent}; })()", "returnByValue": True}) data = final.get("result", {}).get("value", {}) html = await cdp.call("Runtime.evaluate", {"expression": "document.documentElement.outerHTML", "returnByValue": True}) html_text = html.get("result", {}).get("value", "") @@ -476,7 +514,7 @@ async def cdp_transition(ws_url: str, base_url: str, from_page: str, to_page: st document_events = [{"type": event.get("method"), "params": event.get("params", {})} for event in cdp.events if event.get("method") in ("Page.frameNavigated", "Page.loadEventFired")] pre = observations[0] if observations else {} loading_frames = [frame for frame in observations if frame.get("loading")] - return {"frames": observations, "document_navigation_events": document_events, "history_events": data.get("history_events", []), "stable_shell": {"document_marker": {"pre": pre.get("document_marker"), "post": data.get("document_marker")}, "sidebar_marker": {"pre": pre.get("sidebar_marker"), "post": data.get("sidebar_marker")}, "background": {"pre": pre.get("background"), "post": data.get("background")}}, "loading_observed": bool(loading_frames), "prior_body_removed": bool(loading_frames) and all(frame.get("prior_body_visible") is False for frame in loading_frames), "target_page": to_page, "final_agreement": data.get("selected") == to_page and to_page in data.get("body", ""), "final_url": data.get("url", ""), "browser_user_agent": data.get("browser_user_agent", "")} + return {"frames": observations, "document_navigation_events": document_events, "history_events": data.get("history_events", []), "stable_shell": {"document_marker": {"pre": pre.get("document_marker"), "post": data.get("document_marker")}, "sidebar_marker": {"pre": pre.get("sidebar_marker"), "post": data.get("sidebar_marker")}, "background": {"pre": pre.get("background"), "post": data.get("background")}, "surfaces": {"pre": pre.get("surfaces"), "post": data.get("surfaces")}}, "loading_observed": bool(loading_frames), "prior_body_removed": bool(loading_frames) and all(frame.get("prior_body_visible") is False for frame in loading_frames), "target_page": to_page, "final_agreement": data.get("selected") == to_page and to_page in data.get("body", ""), "final_url": data.get("url", ""), "browser_user_agent": data.get("browser_user_agent", "")} def transition_capture(chrome: str, base_url: str, from_page: str, to_page: str, output_dir: Path, devtools_port: int, diff --git a/tests/test_streamlit_browser_evidence.py b/tests/test_streamlit_browser_evidence.py index b39a248..b413fa4 100644 --- a/tests/test_streamlit_browser_evidence.py +++ b/tests/test_streamlit_browser_evidence.py @@ -4,6 +4,7 @@ import json import ssl import sys from pathlib import Path +from typing import Any from unittest.mock import patch @@ -14,6 +15,21 @@ import streamlit_browser_evidence as evidence SECRET_VALUES = ("synthetic-password", "a" * 64, "cookie-value") +SURFACE_STYLE = { + "backgroundImage": "linear-gradient(rgb(1, 2, 3), rgb(4, 5, 6))", + "backgroundColor": "rgb(255, 255, 255)", + "visibility": "visible", + "display": "block", + "opacity": "1", + "rendered": True, +} + + +def surface_records() -> dict[str, dict[str, object]]: + return { + "stAppViewContainer": dict(SURFACE_STYLE), + "stSidebar": dict(SURFACE_STYLE), + } def test_redact_secrets_removes_values_from_nested_manifest() -> None: @@ -39,7 +55,7 @@ def test_validate_transition_manifest_requires_observations() -> None: evidence.validate_transition_manifest(manifest) -def complete_manifest() -> dict[str, object]: +def complete_manifest() -> dict[str, Any]: return { "mode": "transition", "success": True, @@ -51,6 +67,7 @@ def complete_manifest() -> dict[str, object]: "body_text_hash": "hash", "loading": False, "prior_body_visible": True, + "surfaces": surface_records(), }, { "at_ms": 100, @@ -58,6 +75,7 @@ def complete_manifest() -> dict[str, object]: "body_text_hash": "hash2", "loading": True, "prior_body_visible": False, + "surfaces": surface_records(), }, ], "document_navigation_events": [], @@ -66,6 +84,7 @@ def complete_manifest() -> dict[str, object]: "document_marker": {"pre": "doc-1", "post": "doc-1"}, "sidebar_marker": {"pre": "side-1", "post": "side-1"}, "background": {"pre": "rgb(255, 255, 255)", "post": "rgb(255, 255, 255)"}, + "surfaces": {"pre": surface_records(), "post": surface_records()}, }, "loading_observed": True, "prior_body_removed": True, @@ -164,3 +183,47 @@ def test_transition_manifest_rejects_secret_text() -> None: manifest = {"mode": "transition", "success": False, "error": "synthetic-password"} with pytest.raises(ValueError, match="secret"): evidence.validate_transition_manifest(manifest, SECRET_VALUES) + + +def test_validate_transition_manifest_requires_surface_style_evidence_on_every_frame() -> None: + manifest = complete_manifest() + manifest["observations"]["frames"][1]["surfaces"].pop("stSidebar") + with pytest.raises(ValueError, match="surface"): + evidence.validate_transition_manifest(manifest) + + +def test_validate_transition_manifest_requires_stable_surface_style_evidence() -> None: + manifest = complete_manifest() + manifest["observations"]["stable_shell"]["surfaces"]["post"]["stSidebar"]["opacity"] = "0" + with pytest.raises(ValueError, match="surface"): + evidence.validate_transition_manifest(manifest) + + +@pytest.mark.parametrize( + ("field", "value"), + ( + ("backgroundImage", "none"), + ("backgroundColor", ""), + ("visibility", "hidden"), + ("display", "none"), + ("opacity", "0"), + ("opacity", "NaN"), + ("rendered", False), + ), +) +def test_validate_transition_manifest_rejects_non_visible_surface_styles(field: str, value: object) -> None: + manifest = complete_manifest() + observations = manifest["observations"] + for frame in observations["frames"]: + frame["surfaces"]["stAppViewContainer"][field] = value + observations["stable_shell"]["surfaces"]["pre"]["stAppViewContainer"][field] = value + observations["stable_shell"]["surfaces"]["post"]["stAppViewContainer"][field] = value + with pytest.raises(ValueError, match="surface"): + evidence.validate_transition_manifest(manifest) + + +def test_validate_transition_manifest_rejects_missing_stable_shell_markers() -> None: + manifest = complete_manifest() + manifest["observations"]["stable_shell"]["document_marker"] = {"pre": None, "post": None} + with pytest.raises(ValueError, match="shell"): + evidence.validate_transition_manifest(manifest)