fix(dashboard): address PR review 56
This commit is contained in:
@@ -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.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 75 KiB |
@@ -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."
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user