feat(studio): implement open issues N1–N7 (search, timeline, mentions, claims, i18n, local LLM, entity types) - #773
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Blocked merge diagnosis — blocked |
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| JavaScript | Sep 12, 2026 12:49p.m. | Review ↗ | |
| Python | Sep 12, 2026 12:49p.m. | Review ↗ | |
| Shell | Sep 12, 2026 12:49p.m. | Review ↗ | |
| SQL | Sep 12, 2026 12:49p.m. | Review ↗ |
Important
AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.
GitNexus Review · PR #77312 issues found across 11 files. (reviewed 58, 42 triaged of 100 reviewable files) (3 further findings passed review but were held by the noise budget) 🤖 Agent context — 248 symbols · 125 filesSummaryThis appears to be a broad Studio frontend feature set spanning search, timeline, mentions, claims, internationalization, local LLM support, and entity types. Despite having no graph dependents, the change is critical in scope because it modifies many files, symbols, and execution flows across the Studio interface. 🔴 CRITICAL blast radius. A large Studio UI change centered on The core impact is concentrated in the Studio application shell and related interaction components, including
The change spans 125 files and 248 symbols, with impact concentrated in the human named
What changedSymbol Changes (248)
Changed Files (125)
What it affectsArchitecture Impact
What to checkFile Risk (9)
🔀 Structural changes ·
|
Not up to standards ⛔🔴 Issues
|
| Category | Results |
|---|---|
| ErrorProne | 1 medium |
🟢 Metrics 347 complexity · -2 duplication
Metric Results Complexity 347 Duplication -2
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
🔄 What's new in this push (
|
🔄 What's new in this push (
|
🔄 What's new in this push (
|
🔄 What's new in this push (
|
🔄 What's new in this push (
|
🔄 What's new in this push (
|
app-shell: drop redundant single-child fragments in mock render sidebar: declare mocks before vi.mock blocks (used-before-defined)
Adopt GitNexus suggestions on PR #773: - library-view: clear semantic outcome when a new query starts so stale results don't linger during the embedder/model warm-up - schema: EntitySchema.type rejects 'all' (filter sentinel only, never a stored entity type)
…5, JS-0116)
GitNexus thread defects (code-level):
- mention: percent-encode parens in link destinations; decode + guard on
parse so imported ids ending in ')' keep round-tripping (JS-0116 fix
elsewhere removes async-no-await test noise)
- store: commitEntities batches reciprocal writes into ONE history
snapshot; single Undo restores source + backlinks together. Used by
editor save and mindmap child-add (was 2 snapshots, first Undo broke
the reciprocal-link invariant)
- timeline: orphan claims (no matching entity) no longer navigate to a
blank editor - startEdit no-ops without an entity
- entity-types: getEntityTypeDefs/registerEntityType return/store copies;
callers can mutate results without corrupting the cached registry
- entity-type-icon: drop memo - registry updates at runtime, memo pinned
stale icons; render via createElement to satisfy the linter
- vector-store: catch the discarded finally() chain so an aborted build
doesn't surface a second unhandled rejection
- claim-parser: dedupe key is a JSON tuple (NUL delimiter could collide
distinct statements); nested-paren scanLimit confirmed linear
- library-view: semantic failure clears the outcome, falling back to
lexical entities instead of an empty semantic result list
- editor-mention-picker: re-measure on native textarea scroll - the
popover kept its pre-scroll position while the editor scrolled
- schema: StoredEntityTypeSchema shared by Entity/GraphNode/MindMapNode
rejects the 'all' filter sentinel everywhere it is persisted
- right-panel: unique React keys include the index (schema allows
repeated {targetId, relation} objects)
DeepSource (JS-0105/JS-0362/JS-0358/JS-0400/JS-0246 + JS-0116):
- local-adapter: resolveTarget static
- search-worker-client: wireAbort/searchSemantic static
- embeddings: call-signature type alias; drop redundant ctor
- tests: boolean attrs without value; template literal; de-async
no-await test fns (JS-0116 bulk)
right-panel: dedupe rendered links and citations (schema permits
repeated {targetId, relation} objects from imports) so payload-only
React keys stay unique without index-based keys (JS-0437).
editor-view.test: de-async the claims-panel visibility test (JS-0116).
local-adapter: resolveLocalTarget is a module function (JS-0105 no
longer flags it as a this-less class method); send/sendStream reference
this.id for the provider label so the analyzer sees this-usage and the
value stays identical ('local'). vector-store: hoist noop above its
first use (JS-0357 used-before-defined).
editor-mention-picker: scroll-listener cleanup returns a braced body instead of a void shorthand expression.
vector-store: the finally() chain's catch now takes an inline arrow instead of a module-level noop referenced before its declaration text- order, satisfying the used-before-defined analyzer while keeping the original rejection visible on the returned promise.
JS-0067: module-scope function declarations to const arrows across app-shell, mobile-drawer, sidebar, right-panel, voice-input, switch, ai-settings, home-view, library-entities, mindmap, timeline, claims panel, entity grid/table, inbound tests (AGENTS.md preferred style). JS-C1002: i18n message-module exports renamed `t` -> `translate` (16 modules + 31 importers; docs in i18n/t.ts updated). JS-0415/JS-0045/JS-0400: extract nested JSX, brace arrow bodies, omit boolean attr values in tests. JS-R1005: extract mention-query validation, semantic resolution helpers, entity-table/grid subcomponents; fix EntityGrid/GridCard/ EntityTable/TableRow closers left dangling by the mechanical pass. JS-0117: 'u' flag on footer regex; JS-W1042: drop redundant arg; JS-0321: non-empty arrow body in local-adapter test mock.
const arrows do not hoist like function declarations; move component declarations above their users: MobileDrawer last, HomeView last, GridCard before EntityGrid, TableRow before EntityTable. Fixes the used-before-defined findings the JS-0067 conversion introduced.
claim-parser: parenDelta + accept() helpers (CC 6/7 -> low) vector-store: scoreAll/embedBatch/toSearchResults extraction; await the build directly so the slot clears in finally (JS-0098, CC 7/6/6) mention: isValidMentionQuery extraction (CC 9 -> low) right-panel: fn decls to const arrows (JS-0067); RankedResultList/ KeywordResultList/SearchEmptyState/ConnectionList/resolveRankedRowMeta (CC 9/7/6 -> low) sidebar: NavItemButton extraction (CC 7 -> low) mobile-drawer: rankedToEntities helper (CC 9 -> low); consistent-return early form for the media-query listener (JS-0045)
app-shell: extract ViewRouter (CC 13 -> low) editor-claims-panel: extract ClaimRow (CC 10 + JSX depth -> low) ai-harness: EngineOptions/EngineSelect + per-provider default map (CC 13) voice-input: useTranscriptEffects/InterimTranscript/IntentPreview (CC 11) home-view: RecentItemBody (JSX depth) timeline-view: RowIcon/RowTypeBadge (CC 6) library-view: entityForResult/passesTypeFilter/LibraryFooter + async debounce callback (JS-0098 void-chain gone; CC 30/8 -> reduced) editor-view: buildEditorEntity/describeSelection/FORMAT_HANDLERS lookup (CC 29/11/9 -> reduced) right-panel: resolveRankedRowMeta (CC 6) ai-harness test: omit isLoading value (JS-0400)
Editor save now builds the entity with the same id used to derive mention links; the applied review edit had left a stale duplicate block that redeclared mentionLinks/mentions/entity and passed an id-less editing record into buildEditorEntity, breaking typecheck, lint, and the vitest transform. Citation dedupe keys switch to JSON.stringify to avoid delimiter collisions. Co-authored-by: gitnexus-check[bot] <305171802+gitnexus-check[bot]@users.noreply.github.com> 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
…nesting - editor-view: pass the saved entity id into buildEditorEntity instead of letting it mint a second UUID for new entities — the backlink written on each mentioned entity now targets the node that is actually saved, so the reciprocal edge renders in the graph (caught by the new mentions E2E). - graph-view: the display-only entity-type legend intercepted clicks on nodes laid out beneath it; mark it pointer-events-none. - right-panel: the inspector wrapped ConnectionList (which renders its own <ul>) in another <ul>, tripping the axe `list` rule on the mind map page. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
- editor-view: own-property-guarded FORMAT_HANDLERS lookup (object-injection
sink) replaces the unguarded index + always-falsy falsy-check pair.
- ai-harness-settings-panel: exhaustive-switch default-model lookup removes
dynamic indexing and the dead `?? DEFAULT_MODEL.local` branch on a total
Record (unnecessary-conditional).
- library-view / right-panel: braced statement bodies on void arrow shorthands;
the semantic-search timer now explicitly owns its async work via `void (async
() => { ... })()` so no promise is returned where void is expected.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
FORMAT_HANDLERS is now keyed by a closed FormatCommand literal union instead of string, and handleFormat narrows the raw command through an isFormatCommand type guard before the lookup — the dynamic-key function retrieval that Codacy's injection-sink rule flagged is gone entirely, matching the closed-key-space pattern already merged in topbar. 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
Addresses the outstanding GitNexus/DeepSource review threads on PR #773: - search: rename the vector-store import so the client wrapper no longer shadows the imported `semanticSearch` binding; re-validate the shared index against the request's arrays after query embedding so ranking can never score another caller's corpus; abort-responsive wait on an in-flight index build. - ai: race the lazy transformers runtime load against the request signal (send + stream) so an aborted request never starts a model download. - store: apply mutations before pushing history so a batch commit is one undo step and the post-commit state is redone verbatim. - editor: escape backslashes in mention display names, suppress the active-descendant when no suggestion is rendered, extract claims from the live draft rather than the persisted record, re-measure the mention picker on resize. - embeddings: generation guard so a disposed load cannot overwrite a newer runtime's state. - claim-parser: single-pass paren matcher replaces the quadratic rescans. - entity-types: reject the reserved `all` sentinel, validate custom icon values with a component predicate, return copies of cached built-ins. - timeline: fall back when a claim timestamp is blank as well as absent. - library-view/right-panel: honor sort direction and dedupe keys for semantic results, gate the empty state, dedupe-safe React keys. Generated with Codebuff 🤖 Co-Authored-By: Codebuff <noreply@codebuff.com>
Codacy's only required status check flagged two new "Generic Object Injection Sink" warnings on the router's `Record` lookups (`VIEW_NAMES[view]`, `VIEW_ELEMENTS[currentView]`). Both tables are now Maps, so the lookups are method calls instead of dynamic property access, while the single-branch router shape (and its complexity win) is preserved. Generated with Codebuff 🤖 Co-Authored-By: Codebuff <noreply@codebuff.com>
Adds plan 137 (inventory, impact triage, per-finding remediation, CI outcome and the two documented open items: the credential-gated Vercel failure on #773 and the DeepSource JS metric gate) plus the singular month-item label fix for the timeline review finding. Generated with Codebuff 🤖 Co-Authored-By: Codebuff <noreply@codebuff.com>
Follow-up to the GitNexus pass over the remediation commit: - search: `buildVectorIndex` checks `signal.aborted` before the cached-index fast path, so an abort arriving after query embedding still settles as AbortError instead of ranking a cached corpus. - entity-types: the custom-icon predicate accepts only component *types* (function, `react.forward_ref`, `react.memo`); an already-instantiated React element is no longer accepted as a LucideIcon. - claims panel: the extraction dedupe key is JSON-encoded instead of NUL-joined, so unrestricted statements/sources cannot collide. - graph: a registered type with a blank `dot` class falls back to the neutral token instead of rendering an invisible legend marker. - library-view: corrected the promise-lifecycle comment (the IIFE is `void`-ed). Deferred with rationale in plans/137 §5: the semantic type-filter/truncation ordering, Markdown-context mention extraction, the type-selector keyboard trap, and the local-adapter fallback (a consumer-side fix — the adapter contract is covered by 7 tests). Generated with Codebuff 🤖 Co-Authored-By: Codebuff <noreply@codebuff.com>
ae34d57 to
c496a22
Compare
| allEntities, | ||
| claims, | ||
| semanticQuery, | ||
| SEMANTIC_RESULT_LIMIT, |
There was a problem hiding this comment.
🟡 Warning — Retrieve enough ranked documents to fill the entity result cap
vector-store.ts builds one searchable document for every entity and every claim (lines 66-88), and semanticSearch applies its limit to those documents (line 379). This call requests only 100 documents, but resolveSemanticEntities subsequently collapses claim hits to their parent entity (library-view.tsx lines 160-165). Thus, if the highest-ranked 100 documents are claims attached to one entity, the library renders just that entity and omits other relevant entities even though the stated cap is 100 entities.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/studio/views/library-view.tsx, line 100:
<comment>'vector-store.ts' builds one searchable document for every entity and every claim (lines 66-88), and 'semanticSearch' applies its 'limit' to those documents (line 379). This call requests only 100 documents, but 'resolveSemanticEntities' subsequently collapses claim hits to their parent entity (library-view.tsx lines 160-165). Thus, if the highest-ranked 100 documents are claims attached to one entity, the library renders just that entity and omits other relevant entities even though the stated cap is 100 entities.</comment>
<context>Enclosing symbol: useSemanticSearch.</context>
Why this matters: GitNexus flagged this from your code graph — a caller or contract relies on what changed here. · llm-review
| lastIndexedEntities = null | ||
| lastIndexedClaims = null | ||
| indexBuilding = null | ||
| defaultVectorStore.clear() |
There was a problem hiding this comment.
🔴 Error — Invalidate the cached corpus when a replacement build fails
After a successful build for corpus A, a build for corpus B clears defaultVectorStore here but leaves lastIndexedEntities/lastIndexedClaims pointing at A (they are assigned only on the success path at lines 246–247). A later search using A's original array references takes the cached fast path at lines 285–286 despite the store being empty, and therefore returns an empty source: 'semantic' result instead of rebuilding or falling back. This would not be a defect only if a failed replacement build could not follow a successful build, or if another path invalidated the cached references; doBuildIndex permits that sequence and its failure path only clears the store.
Proposed fix (not committable — GitNexus could not verify this fix is local; review call sites before applying):
defaultVectorStore.clear()
lastIndexedEntities = null
lastIndexedClaims = null
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/lib/search/vector-store.ts, line 205:
<comment>After a successful build for corpus A, a build for corpus B clears 'defaultVectorStore' here but leaves 'lastIndexedEntities'/'lastIndexedClaims' pointing at A (they are assigned only on the success path at lines 246–247). A later search using A's original array references takes the cached fast path at lines 285–286 despite the store being empty, and therefore returns an empty 'source: 'semantic'' result instead of rebuilding or falling back. This would not be a defect only if a failed replacement build could not follow a successful build, or if another path invalidated the cached references; </comment>
<context>Enclosing symbol: doBuildIndex. A fix was proposed but NOT verified as local — check the call sites before applying.</context>
Why this matters: GitNexus flagged this from your code graph — a caller or contract relies on what changed here. · llm-review
| label: z.string().min(1).max(64).refine((s) => s.trim().length > 0, { | ||
| message: 'Entity type label must be a non-blank string', | ||
| }), | ||
| color: z.string(), |
There was a problem hiding this comment.
🟡 Warning — Runtime type styling classes are never made available to Tailwind
The registry accepts arbitrary bg, text, and dot class strings from a runtime registration, but the production stylesheet only imports Tailwind (src/app/globals.css:1) and contains neither an @source/safelist nor generated rules for plugin-provided candidates. Tailwind determines utility CSS during the build, before registerEntityType runs, so a plugin registering a class absent from the scanned application sources will render its type with no corresponding styles. This would not be a defect only if every plugin were restricted to already-compiled classes or shipped its own CSS; the API/documentation instead exposes these as unconstrained Tailwind token strings for runtime plugins (plans/135-n7-entity-types.md:8-18, 46-64).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/lib/studio/entity-types.ts, line 69:
<comment>The registry accepts arbitrary 'bg', 'text', and 'dot' class strings from a runtime registration, but the production stylesheet only imports Tailwind ('src/app/globals.css:1') and contains neither an '@source'/safelist nor generated rules for plugin-provided candidates. Tailwind determines utility CSS during the build, before 'registerEntityType' runs, so a plugin registering a class absent from the scanned application sources will render its type with no corresponding styles. This would not be a defect only if every plugin were restricted to already-compiled classes or shipped its own CSS; t</comment>
<context>Enclosing symbol: EntityTypeDefSchema.</context>
Why this matters: GitNexus flagged this from your code graph — a caller or contract relies on what changed here. · llm-review
| console.error(`entity-types: cannot register built-in entity type "${id}"`) | ||
| return | ||
| } | ||
| customTypeDefs.set(id, { ...parsed.data }) |
There was a problem hiding this comment.
🟡 Warning — Registering after mount does not update any rendered type lists or metadata
registerEntityType mutates only a module-local Map; it neither exposes a subscription nor updates React state. Renderers such as TypeSelector read getEntityTypeDefs() only during their existing render (src/components/studio/views/type-selector.tsx diff, new line 42), so a plugin that registers a type after that component is mounted leaves the currently displayed selector/legend/cards unchanged until an unrelated state update happens. This would not be a defect if registration were guaranteed to complete before all relevant components mount, but the documented goal explicitly promises runtime registration and says registered types “immediately appear” (plans/135-n7-entity-types.md:8-10, 62-64).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/lib/studio/entity-types.ts, line 126:
<comment>'registerEntityType' mutates only a module-local 'Map'; it neither exposes a subscription nor updates React state. Renderers such as 'TypeSelector' read 'getEntityTypeDefs()' only during their existing render ('src/components/studio/views/type-selector.tsx' diff, new line 42), so a plugin that registers a type after that component is mounted leaves the currently displayed selector/legend/cards unchanged until an unrelated state update happens. This would not be a defect if registration were guaranteed to complete before all relevant components mount, but the documented goal explicitly promises</comment>
<context>Enclosing symbol: registerEntityType.</context>
Why this matters: GitNexus flagged this from your code graph — a caller or contract relies on what changed here. · llm-review
| // today's stay-in-editor behavior. | ||
| if (mentions.length > 0) { | ||
| finishEditing() | ||
| navigateToView('library') |
There was a problem hiding this comment.
⚪ Nit — Do not navigate before preventing the draft unmount flush
handleSave removes draftIdRef.current at line 361, but this new navigation unmounts the editor. useEditorDraft registers an unmount cleanup that always calls flushDraft() (src/components/studio/editor-hooks.tsx:160-173), and flushDraft() rebuilds and saves a draft using that unchanged ref (lines 161-169). Consequently, saving an entity with a mention removes its draft and immediately recreates it during unmount.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/studio/views/editor-view.tsx, line 367:
<comment>'handleSave' removes 'draftIdRef.current' at line 361, but this new navigation unmounts the editor. 'useEditorDraft' registers an unmount cleanup that always calls 'flushDraft()' (src/components/studio/editor-hooks.tsx:160-173), and 'flushDraft()' rebuilds and saves a draft using that unchanged ref (lines 161-169). Consequently, saving an entity with a mention removes its draft and immediately recreates it during unmount.</comment>
<context>Enclosing symbol: EditorView.</context>
Why this matters: GitNexus flagged this from your code graph — a caller or contract relies on what changed here. · llm-review
…779) The #773 squash merge landed pnpm-lock.yaml with unresolved conflict markers plus a stale overrides block. pnpm ≥8 auto-merges the markers in memory so GitHub Actions stayed green, but every Vercel deployment for that head failed (0 markers at the last green deploy, 3 at the failing one). Removing the block is a pure deletion — no resolution changes, so the deliberate adm-zip exclusion and sharp/transformers pins are kept. Co-authored-by: CI/CD Tester <ci-cd-test@example.com>

Summary
Implements the seven open issues:
Xenova/paraphrase-multilingual-MiniLM-L12-v2, 384-d, q8/WASM) with a library toggle and graceful lexical BM25 fallback. Runs on the main thread: the search worker's dynamicimport('@huggingface/transformers')never resolves under Turbopack's module-worker bundling (verified dev + production via Playwright traces — worker constructed, zero network, promise hangs forever); the worker semantic path was removed, lexical SEARCH/RESET stays on the worker.dks://entity/mention tokens, inline mention picker, reciprocal backlinks on save (projected on the graph). Fixes react-markdown'surlTransformstrippingdks://URIs.Assertion: ... (Source: ...)extraction into structured claim rows, with dedupe.makeTmessage-module layer covering settings/storage surfaces.localprovider (transformers.js,onnx-community/Qwen2.5-0.5B-Instruct, q4) with zero API key, no network.registerEntityType/getEntityTypeMetareplacing the staticENTITY_TYPE_METAmap;Entity.typewidened.Also: 44x44 touch target for the Radix switch (WCAG 2.5.5).
Fixes #751
Fixes #752
Fixes #753
Fixes #754
Fixes #755
Fixes #756
Fixes #757
Verification
pnpm run lint— cleanpnpm run typecheck— cleanpnpm run test— 2557 passed (166 files)pnpm run build— clean (Turbopack)./scripts/verify-deps.sh— ALL CHECKS PASSEDpnpm run test:e2e— 590 passed, 2 skipped, 0 failedpnpm run test:coverage— 86.13% statementsNote
Rebased onto
mainafter #772 merged; the only conflict was thegraph-view.tsximport block (combined #772'sGraphEdge/GraphNodetype imports with the registry imports).📝 Summary by GitNexus
Summary
This appears to be a broad Studio frontend feature set spanning search, timeline, mentions, claims, internationalization, local LLM support, and entity types. Despite having no graph dependents, the change is critical in scope because it modifies many files, symbols, and execution flows across the Studio interface.
🔴 CRITICAL blast radius. A large Studio UI change centered on
src/components/studio/, reaching 69 affected flows across theStudioandViewsareas.The core impact is concentrated in the Studio application shell and related interaction components, including
AppShell,MindMapView,getViewName,currentView, andhandleViewErrorinsrc/components/studio/app-shell.tsx. Review the view selection and error handling paths first, then the navigation and command interactions insrc/components/studio/command-palette.tsxandsrc/components/studio/mobile-drawer.tsx.src/components/studio/right-panel.tsxis the hottest changed file, with 15 changed symbols. The PR also alters editor behavior throughbuildDraftanduseEditorDraftinsrc/components/studio/editor-hooks.tsx, entity rendering throughEntityIconinsrc/components/studio/entity-type-icon.tsx, and adds end to end coverage undere2e/for claims, editor mentions, entity types, local LLM, semantic search, and timeline behavior.The change spans 125 files and 248 symbols, with impact concentrated in the human named
StudioandViewsmodules and smaller touches inAi,Ui,Search, andEditor. There are no graph dependents or cross-repo consumers, so review attention should stay on internal integration among the changed Studio components and the affected execution flows.Added by GitNexus for PR #773. Edit freely — this block is replaced on the next review, everything above it is left untouched.