Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions docs/product-technical-gap-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,79 @@
- **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).

## 4. 2026-08-23 Audit: Accessibility, i18n, and Release-Process Gaps

A dedicated multi-agent audit (independently verified against live source and
cross-checked against the full PR history to rule out duplicates) found 14
new, previously-untracked gaps. None of these overlap the sections above.

### 4a. Accessibility (frontend)
- **Post detail popup has no dialog semantics on `main`** (true when this
audit ran): `PostDetailPopup` in `frontend/src/App.tsx` rendered a
backdrop/panel with no `role="dialog"`, `aria-modal`, `aria-labelledby`,
Escape-to-close, or focus trap/restore. **Already fixed, not yet on
`main`**: `docs/customer-master-scope-adr` (verified at commit
`7d6cab694d7d6998a2b5ecf8fb1ed2c704052411`) already has a complete
implementation -- `popupPanelRef`, `role="dialog"`/`aria-modal`, Tab-cycle
wraparound that filters hidden/`aria-hidden`/closed-`<details>` elements,
Escape-to-close, and focus-restore-on-unmount via effect cleanup. No new
PR needed here; this resolves once that branch reaches `main`.
- **Async error feedback isn't announced**: 17 of 19 `className="error"`
sites in `App.tsx` render as plain `<p>` with no `role="alert"`/`aria-live`
(Ask, Keymen extraction, lineage rebuild, Customer Master, Ask Agent, and
more). Fixed in the accessibility-sweep PR referenced below.
- **Two inputs have no accessible name**: the "Ask about this lineage"
question field and the new-ticket-title field use only a `placeholder`,
unlike the adjacent due-date field. Fixed in the same sweep.
- **AdminPanel save result isn't announced**: the save-success/error `<span>`
in `AdminPanel.tsx` carries no `role`/`aria-live`. Fixed in the same sweep.
- **Event Lineage DAG node kind (root/branch/regular) is color-only**: no
textual indication in the `aria-label`, tooltip, or a legend; not fixed by
the existing DAG test/story coverage (that PR explicitly found no bug).
Needs a legend/label design decision, not yet fixed.
- **DAG keyboard focus ring is very weak**: `.lineage-dag-node:focus` strips
the native outline and replaces it with only a 1px stroke-width bump in the
same border color. Fixed in the accessibility-sweep PR.
- **Rendered post-body tables have no header semantics**: both the
structured-unit table renderer and the OCR-image-text table renderer in
`PostBody.tsx` emit only `<td>`, no `<th scope="col">`/`<caption>`. Was
present in a prior merged PR (#303) but silently dropped by a later
whole-file rewrite (commit `ef6f5a5f`). Not yet re-fixed.

### 4b. i18n
- **Unregistered aria-label key**: `tf("Affiliates of {name}")` (the
customer-entity-tree `aria-label` in `App.tsx`) has no entry in any of the
ko/zh/ja/vi locale blocks in `i18n.ts`, so it always renders in English
regardless of the active locale. Fixed in the accessibility-sweep PR.

### 4c. Release-process / CHANGELOG hygiene
- **CHANGELOG.md stalled at `[2.12.6]`** while the tree already contains ~80
additional ADRs and whole undocumented feature surfaces (Global Ask
evidence workspace, the buyer-facing global nav). No version header past
2.12.6 exists anywhere in the file. Large, needs careful reconstruction;
logged here rather than attempted in this pass.
- **9 already-drafted `CHANGELOG.d/` fragments (2.12.7 through 2.21.1) were
never compiled** into `CHANGELOG.md` — the release text already exists,
the compile step was simply never run. Fixed in a dedicated compilation PR
(mechanical, low-risk since the prose already exists).
- **The `ef6f5a5f` squash-merge (PR #347) left at least one shipped feature
undocumented**: the durable post-content ingestion queue (ADR 0098) has no
CHANGELOG.md entry and no `CHANGELOG.d/` fragment, unlike its sibling
changes in the same commit. Logged, not yet fixed.
- **PR #460's own fix is missing from `CHANGELOG.md`'s `[Unreleased]`
section.** Will be added alongside the CHANGELOG.d compilation PR.
- **Version fields agree with CHANGELOG (2.12.6) only because both are
equally stalled**, not because the release process is healthy — real
merged work already sits past that version with no compiled release.
Same root cause as the two items above; resolved once a batch-release pass
runs.

### 4d. Test coverage
- **Bookmark endpoints (`GET`/`POST /api/posts/{post_id}/bookmark`) have zero
HTTP-level test coverage.** The only existing tests exercise the lower-level
`_load_visible_post` helper directly, bypassing FastAPI entirely — the
route wiring, request validation, and the `post_bookmark` insert/delete SQL
introduced by a recent fix are unexercised. Needs a `TestClient`-level
test; not yet added.

*This document is continuously updated by the hourly automated agent loop.*
8 changes: 7 additions & 1 deletion frontend/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,25 @@ beforeEach(() => {

afterEach(() => {
vi.unstubAllGlobals();
window.sessionStorage.clear();
window.localStorage.clear();
});

describe("App, unauthenticated", () => {
it("shows a login button that starts the real OIDC redirect", async () => {
window.history.replaceState({}, "", "/?post=abc#details");
render(<App showLabPanels />);
const button = screen.getByRole("button", { name: /log in/i });
await userEvent.click(button);
expect(signinRedirect).toHaveBeenCalledTimes(1);
expect(signinRedirect).toHaveBeenCalledWith(
expect.objectContaining({
state: expect.objectContaining({ returnUrl: expect.stringMatching(/^\//) }),
state: expect.objectContaining({ returnUrl: "/?post=abc#details" }),
}),
);
// Persisted as a fallback in case the OIDC state round-trip is dropped
// (see oidcReturnUrl.ts's restoreOidcReturnUrl, consumed in main.tsx).
expect(window.sessionStorage.getItem("lineageweave.oidc.returnUrl")).toBe("/?post=abc#details");
});
});

Expand Down
4 changes: 2 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4610,7 +4610,8 @@ export default function App({ showLabPanels = false }: { showLabPanels?: boolean
</div>
<div className="login-controls">
<button className="btn-primary" onClick={() => {
const returnUrl = window.location.pathname + window.location.search;
const returnUrl = returnUrlFromLocation();
rememberOidcReturnUrl(returnUrl);
Comment thread
seonghobae marked this conversation as resolved.
void auth.signinRedirect({ state: { returnUrl } });
}}>
{t("Log in")}
Expand All @@ -4620,7 +4621,6 @@ export default function App({ showLabPanels = false }: { showLabPanels?: boolean
<small>Enterprise SSO Authentication</small>
</div>
</div>
Comment thread
seonghobae marked this conversation as resolved.
{destination === "admin" ? <AdminPanel currentBrandName={brandName} onBrandNameChange={setBrandName} accessToken={accessToken} /> : null}
Comment thread
seonghobae marked this conversation as resolved.
</main>
<footer className="app-footer" role="contentinfo">
<div className="app-footer-title">
Expand Down
Loading