From c7ca106b135d60e037c9f15ba5e7d118b43021f3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 12:42:03 +0900 Subject: [PATCH 01/65] fix: align buyer lineage shell with UI UX guide --- docs/product-technical-gap-baseline.md | 173 +++++++++++++++++--- docs/storybook-inventory.md | 1 + frontend/src/App.css | 212 ++++++++++++++++++++++++- frontend/src/App.test.tsx | 12 ++ frontend/src/App.tsx | 44 ++++- frontend/src/LineageDag.stories.tsx | 60 +++++++ frontend/src/LineageDag.tsx | 184 +++++++++++++-------- frontend/src/components/BuyerNav.tsx | 10 +- frontend/src/i18n.ts | 10 ++ 9 files changed, 612 insertions(+), 94 deletions(-) create mode 100644 frontend/src/LineageDag.stories.tsx diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index e65883463..fa150e44b 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,26 +1,151 @@ # Product & Technical Gap Baseline -## 1. Known Parsing & Frontend Display Gaps -- **Footnote Parsing**: `post=00505695-3e61-1fd1-83c5-263f88a9e77a` fails to recognize footnotes (li/oi level errors). -- **Table Parsing**: `post=00505695-3e61-1fd1-80c6-86bb61c8ddc5` completely fails at parsing tables. -- **Indentation**: Incorrect indentation rendering in `post=00505695-7571-1fd1-83c3-d521b187ad5b` and `post=00505695-3e61-1fd1-83c0-497b3c1c455e`. -- **Image/Table OCR**: `post=00505695-7571-1fd1-83dd-3d22a61a5734` fails text recognition for tables inside images, markdown parsing fails, and image OCR description is too shallow for Ontology & Semantics. -- **Math/Superscripts**: `post=00505695-9612-1fe1-83a7-e30153323f25` fails to parse superscripts like m^3 properly. Needs strict Ontology grammar for math formulas. -- **Missing UI Elements**: DAG (Directed Acyclic Graph) view is currently missing from the frontend for `post=00505695-7571-1fd1-83c5-895ed333cdbc`. - -## 2. LLM Extraction & Knowledge Graph Gaps -- **Multiple Project Extraction**: (Resolved) LLM prompt updated to request key_events as objects with project_name, separating events correctly. -- **5W1H Missing**: (Resolved) LLM prompt updated to explicitly request 5W1H evidence items in the JSON output array. -- **R&R and Keyman Missing**: (Resolved) LLM prompt updated to explicitly instruct using actual stated names rather than collective titles. -- **Entity Resolution / Searxng**: Abbreviations like "한전" and "한국전력" are not mapped properly using Searxng and KG corroboration. -- **Meso-level Team Mapping**: (Resolved) Checked extraction logic; `team` mapping logic is present and correct, but LLM needed better explicit instruction which is covered by R&R resolution. -- **Base64 Image Omni-modal**: Current text-only embedding fails on images. Omni-modal LLM processing is required for images to capture layout, font size, colors, and spatial meaning. - -## 3. General Architecture Gaps -- **DB Architecture**: Ensure PostgreSQL is strictly used (no file DBs), 3rd normal form is maintained, and Hot Partitions are handled. DB locks must be managed (or use read/write replicas). -- **Zotero Integration**: Papers and standards referenced by TEPP must be synced via Local Zotero API (http://localhost:23119/api/) and cited using APA 7th edition in docstrings. -- **Testing**: We need actual testing of Psychometrics (Fast-MLSIRM parameter calibration, RMSE of estimates, Fixed-Item Parameter Calibration, CAT) against synthetic/demo data. -- **Security & Compliance**: PII masking cannot break the system. Need SOC 2 and CSAP compliance alternatives to blind PII masking. -- **LLM Orchestration**: Ensure ALL LLM calls route through `contextual-orchestrator` utilizing API keys (BYTEZ, NVIDIA, OPENROUTER, OPENAI) with auto model discovery and optimal reasoning effort allocation (Fugu/Conductor/TRINITY research). - -*This document is continuously updated by the hourly automated agent loop.* +> Audit scope: the current `feat/uiux-standard-guide-v3-restacked` worktree, +> compared with `origin/main`, the UI/UX Standard Guide v3.0 supplied for this +> product, ADR 0118, the accepted TEPP PRD/contracts, and the +> contextual-orchestrator architecture. Real source identifiers are deliberately +> replaced with case labels; they must not enter repository artifacts. + +## 1. Exact-head evidence + +Audit anchor: the exact source state carried by this commit at 2026-08-21; +record the final PR head with `git rev-parse HEAD` during acceptance. + +- **Implemented in source:** PostgreSQL-backed API boundaries, Keyverse/OIDC + identity boundary, buyer navigation, post popup, ABAC/RBAC surfaces, Korean + summary, 5W1H, R&R/Keyman, customer hierarchy, tickets/calendar, chat, + provenance/evidence, and reconstructed lineage API/DAG layout. +- **Implemented in source, runtime evidence still required:** TEPP import/API + transport, contextual-orchestrator LLM/VISION transport, SearXNG + corroboration, Local Zotero ingestion, real PostgreSQL import, and browser + login-to-evidence workflows. A source adapter or a synthetic test is not live + integration proof. +- **Figma reference:** ADR 0118 records file `1Su3lDRmiZdcUs47t1QwIX`; the + inspected Event Lineage frames are desktop `5:14` and mobile `5:15`. +- **Current PR gate:** PR #347 is review-required; Devin Review failed, the + required OpenCode/Noema reviews and product/security checks were pending at + audit time, and CodeRabbit skipped review because the diff exceeded its file + limit. This is not merge-ready evidence. + +## 2. UI/UX Standard Guide v3.0 comparison + +### 2.1 Satisfied or substantially present + +- Desktop shell has a sticky header, top-right user/logout controls, GNB, + footer with brand/copyright, standard breakpoints, 1920px maximum layout, + Noto Sans family, CI/BI palette tokens, table alignment tokens, focus styles, + required-field marker, and 50% modal backdrop. +- GNB active state is exposed with `aria-current`; the lineage DAG has keyboard + activation and branch/root/current visual states. +- PostgreSQL, orchestrator, TEPP, provenance, and synthetic-fixture boundaries + are documented in `ARCHITECTURE.md` and the applicable ADRs. + +### 2.2 Gaps and status + +- **Mobile drawer — fixed in this worktree:** CSS referenced a drawer trigger but + the authenticated shell rendered no trigger or drawer. The shell now renders + an accessible hamburger button, close action, overlay, and reusable BuyerNav. +- **Event Lineage Figma parity — fixed in this worktree:** the DAG now includes + buyer-evidence context, legend, horizontal overflow on phones, inference + boundary, direction markers, and an evidence trail table/cards treatment. +- **Approved CI/BI asset — open:** the header/footer currently render the + tenant brand name as text. Do not invent or alter a corporate logo; add the + approved asset only after the tenant CI/BI source and usage permission are + available. +- **Header utilities/search — partial:** search exists on the board, but a + global header search and site-map utility are not yet implemented. +- **Locale document metadata — open:** `frontend/index.html` has a fixed + `lang="en"`; it should follow the selected locale for accessibility. +- **Phone content affordance — runtime verify:** the guide requires a visible + portion of content below the fold. Verify this with Playwright at 390px after + the authenticated runtime is available. + +## 3. Supplied parsing and semantic cases + +The following user-reported cases remain tracked without storing real post IDs: + +- `case-footnote-01`: footnote/list `li`/`ol` boundary is misclassified. +- `case-table-01`: HTML table parsing fails. +- `case-indent-01` and `case-indent-02`: semantic indentation is wrong. +- `case-multi-project-01`: two projects must produce separate event streams; + internal facilities must not be guessed as Partner/Supplier. +- `case-image-table-01`: image tables need region-aware OCR/description and + rendered Markdown/table support. +- `case-summary-affiliation-01`: a role such as PM needs person, title, and + organization evidence rather than an unqualified collective label. +- `case-r-and-r-01`: requester, assignee, action, and cost/payment owner must + remain explicit in R&R evidence. +- `case-math-01`: units such as m³ need superscript-preserving source/semantic + representations and ontology-safe rendering. + +These are not “resolved” merely because a prompt or heuristic was changed. +Each requires synthetic unit coverage plus an authorized runtime reproduction +or an explicit unavailable result. + +## 4. Product and technical gaps + +- **Entity and abbreviation resolution — open:** canonical names, aliases, + multilingual labels, team-vs-organization typing, title-aware person + disambiguation, and SearXNG/internal corroboration need end-to-end evidence. +- **Image/HTML semantic units — partially implemented:** source DOM, layout + metadata, region evidence, and provenance must remain separate from embedding + text; transparent/unsupported image conversion and multimodal processing need + live verification. +- **TEPP measurement — boundary accepted, runtime open:** LineageWeave must + call TEPP through its published import/REST contract and must not implement a + local theta, psychometric calibration, CAT, or judge score. TEPP owns the + Rust numerical/psychometric layer and its multilevel/multiple-membership/time + model. +- **Orchestration — boundary accepted, runtime open:** all LLM/VISION/embedding + work must cross contextual-orchestrator with provenance, session, cost, schema + validation, synthesis/repair, and capability discovery. No provider key or + model selector belongs here. +- **Authorization — runtime open:** verify actual Keyverse/OIDC login, corp/PU + attributes, post visibility, ABAC/RBAC denials, and no cross-tenant evidence + leakage using aggregate, non-identifying results. +- **Database/operations — audit open:** verify 3NF constraints, hot-partition + behavior, lock boundaries, Valkey event delivery, multithreaded server + behavior, retention grants, and read/write contention on the local Compose + stack. +- **Literature/Zotero — open:** record APA 7 references and verify Local Zotero + API availability before claiming synchronization. The repository must retain + only metadata/citations appropriate for public artifacts. +- **Release/quality gates — open:** current PR checks and required reviews must + complete on the exact current head; frontend, backend, browser, accessibility, + Storybook, security, and coverage evidence must be collected before release. + +## 5. Next acceptance loop + +1. Re-fetch the exact PR head and required reviews/checks. +2. Run frontend lint, tests, build, Storybook, backend tests, and authenticated + browser checks when the local stack is available. +3. Reproduce each case label using synthetic fixtures or authorized runtime + aggregates, preserving `unavailable` as an explicit result. +4. Fix only evidence-backed failures, then repeat the exact-head protected merge + gate. Do not self-approve, bypass protection, or claim a PR is merged without + a merge SHA. + +## 6. References (APA 7th) + +ContextualWisdomLab. (2026). *TEPP* [Computer software]. GitHub. +https://github.com/ContextualWisdomLab/TEPP + +ContextualWisdomLab. (2026). *contextual-orchestrator architecture notes* +[Computer software]. GitHub. +https://github.com/ContextualWisdomLab/contextual-orchestrator/blob/main/docs/architecture.md + +Nielsen, S., Cetin, E., Schwendeman, P., Sun, Q., Xu, J., & Tang, Y. (2025). +*Learning to orchestrate agents in natural language with the Conductor*. +arXiv. https://doi.org/10.48550/arXiv.2512.04388 + +Tang, Y., Cetin, E., Xu, J., Sun, Q., Nielsen, S., Richard, V., Goda, H., +Tymchenko, I., Nguyen, N., Lee, H., Ashiga, M., Kotyan, S., & Kuroki, S. +(2026). *Sakana Fugu technical report*. arXiv. +https://arxiv.org/abs/2606.21228 + +Xu, J., Sun, Q., Schwendeman, P., Nielsen, S., Cetin, E., & Tang, Y. (2025). +*TRINITY: An evolved LLM coordinator*. arXiv. +https://doi.org/10.48550/arXiv.2512.04695 + +World Wide Web Consortium. (2024). *Web Content Accessibility Guidelines + (WCAG) 2.2* (W3C Recommendation). https://www.w3.org/TR/WCAG22/ diff --git a/docs/storybook-inventory.md b/docs/storybook-inventory.md index 28c59bd48..ed8e3d202 100644 --- a/docs/storybook-inventory.md +++ b/docs/storybook-inventory.md @@ -9,6 +9,7 @@ buyer-facing control you can click before changing product CSS. | `AnalysisRun/CutoffKnownBody` | Read the cutoff-known sentence, then compare it with the live body below. | `--color-accent-border`, `--space-panel-block`, `--radius-panel`, `CutoffKnownBody` | | `Analysis/LineageEntityPicker` | Choose which corp to reconstruct, then click Request a lineage reconstruction. | `--space-control-gap`, `--size-control-min`, `--radius-control`, `LineageEntityPicker` | | `Chrome/PopupCloseButton` | Close the evidence panel or post popup. | `--space-close-inset`, `--font-size-close`, `PopupCloseButton` | +| `Evidence/LineageDag` | Inspect a branching Event Lineage, then open a record or read its evidence trail. | `--color-primary`, `--color-accent-orange`, `LineageDag` | Repeated web objects must use `frontend/src/styles/tokens.css` and a module under `frontend/src/components/`. Do not add a second Node package manager; diff --git a/frontend/src/App.css b/frontend/src/App.css index c72aab078..0c13e4c40 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -117,6 +117,38 @@ color: var(--color-text-heading); } +.mobile-drawer-backdrop { + position: fixed; + inset: 0; + z-index: var(--z-drawer-backdrop); + background: rgba(0, 0, 0, 0.5); +} + +.mobile-drawer { + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: min(20rem, 88vw); + padding: 1rem; + background: var(--surface); + box-shadow: var(--shadow); + overflow-y: auto; + z-index: var(--z-drawer); +} + +.mobile-drawer-close { + display: block; + margin-left: auto; + min-width: var(--size-control-min); + min-height: var(--size-control-min); + border: 0; + background: transparent; + color: var(--text-h); + font-size: 1.5rem; + cursor: pointer; +} + /* App Footer (§2.2.3 & §2.2.4) */ .app-footer { margin-top: auto; @@ -487,6 +519,65 @@ margin: 0 0 1.25rem; } +.lineage-dag-header { + margin-bottom: 1rem; +} + +.lineage-dag-header h4 { + margin: 0.15rem 0 0.35rem; +} + +.lineage-dag-description { + margin: 0; + color: var(--text-muted); + font-size: 0.9rem; +} + +.lineage-dag-legend { + display: flex; + flex-wrap: wrap; + gap: 0.75rem 1rem; + margin-bottom: 1rem; + color: var(--text-muted); + font-size: 0.8rem; +} + +.lineage-dag-legend-item { + display: inline-flex; + align-items: center; + gap: 0.35rem; +} + +.lineage-dag-legend-mark { + display: inline-block; + width: 0.7rem; + height: 0.7rem; + border: 2px solid var(--border); + border-radius: 50%; + background: var(--surface-muted); +} + +.lineage-dag-legend-root { + border-color: var(--color-accent-info); +} + +.lineage-dag-legend-branch { + border-color: var(--color-accent-orange); + background: var(--badge-actor-organization-bg); +} + +.lineage-dag-legend-current { + border-color: var(--text-h); + border-width: 3px; +} + +.lineage-dag-viewport { + overflow-x: auto; + border: 1px solid var(--border); + border-radius: 8px; + background: var(--surface); +} + .lineage-dag-group figcaption { font-size: 0.85rem; opacity: 0.8; @@ -494,15 +585,15 @@ } .lineage-dag svg { - border: 1px solid var(--border); - border-radius: 8px; - background: var(--surface); + display: block; + min-width: 100%; } .lineage-dag-edge { stroke: var(--border); stroke-width: 1.5; fill: none; + marker-end: url("#lineage-dag-arrow"); } .lineage-dag-node { @@ -543,6 +634,38 @@ stroke: var(--text-h); } +.lineage-dag-inference-note { + margin: 1rem 0; + padding: 0.75rem 1rem; + border-left: 3px solid var(--color-primary); + background: var(--color-accent-background); + color: var(--text); + font-size: 0.85rem; +} + +.lineage-dag-inference-note strong { + display: block; + margin-bottom: 0.2rem; + color: var(--text-h); +} + +.lineage-dag-evidence { + margin-top: 1rem; +} + +.lineage-dag-evidence h4 { + margin-bottom: 0.5rem; +} + +.lineage-dag-evidence table { + width: 100%; +} + +.lineage-dag-evidence td:last-child { + text-align: right; + font-variant-numeric: tabular-nums; +} + .keyman-list { list-style: none; padding: 0; @@ -836,6 +959,89 @@ display: block; } + .mobile-drawer .buyer-gnb { + display: flex; + flex-direction: column; + align-items: stretch; + height: auto; + margin: 0.5rem 0 0; + padding: 0; + gap: 0.25rem; + border-bottom: 0; + } + + .mobile-drawer .buyer-gnb-item { + justify-content: flex-start; + width: 100%; + min-height: 3rem; + height: auto; + padding: 0.75rem; + border-radius: var(--radius-control); + } + + .mobile-drawer .buyer-gnb-item[aria-current="page"]::after { + display: none; + } + + .mobile-drawer .buyer-gnb-item[aria-current="page"] { + background: var(--color-accent-background); + } + + .mobile-drawer .buyer-gnb-tools { + margin: 0.75rem 0 0; + padding-top: 0.75rem; + border-top: 1px solid var(--border); + } + + .lineage-dag-viewport { + padding-bottom: 0.25rem; + } + + .lineage-dag svg { + min-width: 42rem; + } + + .lineage-dag-evidence td { + display: block; + text-align: left !important; + } + + .lineage-dag-evidence tr { + display: block; + margin-bottom: 0.5rem; + border: 1px solid var(--border); + border-radius: var(--radius-control); + background: var(--surface); + } + + .lineage-dag-evidence th { + display: none; + } + + .lineage-dag-evidence td { + border: 0; + } + + .lineage-dag-evidence td::before { + display: block; + margin-bottom: 0.15rem; + color: var(--text-muted); + font-size: 0.75rem; + font-weight: 700; + } + + .lineage-dag-evidence td:nth-child(1)::before { + content: "Graph relation"; + } + + .lineage-dag-evidence td:nth-child(2)::before { + content: "When"; + } + + .lineage-dag-evidence td:nth-child(3)::before { + content: "Evidence (fused_score)"; + } + .app-header { padding: 0 1rem; } diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 7462abd2c..b777aa549 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -2002,6 +2002,9 @@ describe("App, authenticated", () => { const eventLineage = within(popup as HTMLElement).getByRole("heading", { name: "Event Lineage" }); const affiliate = within(popup as HTMLElement).getByRole("heading", { name: "Affiliate tree" }); const keyman = within(popup as HTMLElement).getByRole("heading", { name: "Keymen" }); + expect(within(popup as HTMLElement).getByText("BUYER EVIDENCE")).toBeInTheDocument(); + expect(within(popup as HTMLElement).getByText("Inference boundary")).toBeInTheDocument(); + expect(within(popup as HTMLElement).getByRole("table", { name: "Evidence trail" })).toBeInTheDocument(); expect(evaluation.compareDocumentPosition(eventLineage) & Node.DOCUMENT_POSITION_FOLLOWING).not.toBe( 0, ); @@ -3508,5 +3511,14 @@ describe("App, authenticated", () => { expect(await screen.findByRole("navigation", { name: "Buyer navigation" })).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Board" })).toHaveAttribute("aria-current", "page"); expect(screen.queryByText("Advanced review tools")).not.toBeInTheDocument(); + const mobileMenu = screen.getByRole("button", { name: "Open navigation" }); + expect(mobileMenu).toHaveAttribute("aria-expanded", "false"); + await userEvent.click(mobileMenu); + expect(screen.getAllByRole("button", { name: "Close" })).toHaveLength(2); + expect(document.getElementById("mobile-buyer-navigation")).toBeInTheDocument(); + const drawerClose = document.querySelector(".mobile-drawer-close"); + expect(drawerClose).not.toBeNull(); + await userEvent.click(drawerClose as HTMLButtonElement); + expect(screen.getByRole("button", { name: "Open navigation" })).toHaveAttribute("aria-expanded", "false"); }); }); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6fba0dd41..5615f05ff 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -101,7 +101,6 @@ import { tf, useLocale, } from "./i18n"; -import { rememberOidcReturnUrl, returnUrlFromLocation } from "./oidcReturnUrl"; import "./App.css"; function orchestratorUnavailableMessage(err: unknown, action: string): string { @@ -4553,6 +4552,7 @@ export default function App({ showLabPanels = false }: { showLabPanels?: boolean const [brandName, setBrandName] = useState("LineageWeave"); const auth = useAuth(); const [destination, setDestination] = useState("board"); + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [postToOpen, setPostToOpen] = useState(() => { if (typeof window === "undefined") return null; return new URLSearchParams(window.location.search).get("post"); @@ -4563,6 +4563,10 @@ export default function App({ showLabPanels = false }: { showLabPanels?: boolean // post_admin check (`canRebuild`), not on this caller-supplied prop. const testOnlyLabPanels = import.meta.env.MODE === "test" && showLabPanels; const accessToken = auth.user?.access_token; + const changeDestination = (nextDestination: BuyerDestination) => { + setDestination(nextDestination); + setMobileMenuOpen(false); + }; useEffect(() => { if (accessToken) { @@ -4620,7 +4624,6 @@ export default function App({ showLabPanels = false }: { showLabPanels?: boolean Enterprise SSO Authentication - {destination === "admin" ? : null}