Skip to content

Latest commit

 

History

History
850 lines (658 loc) · 55 KB

File metadata and controls

850 lines (658 loc) · 55 KB

Repository TODO Roadmap

I treated the attached audit prompt as the operating spec for this pass and inspected the ZIP snapshot at:

/mnt/data/Windows-Venice-API-connector-clean-20260621-070025.zip

This is a verified repo-snapshot audit, not a mystical claim that every byte has confessed its sins. I did not modify source files, but local validation commands generated node_modules/, dist/, dist-electron/, and .config/*.local.yaml during execution, because software likes leaving crumbs.


1. Current-State Summary

What this repo appears to do

Venice Forge is an Electron + Vite + React + TypeScript local-first desktop/web workspace for the Venice API. It includes chat, streaming, image/media tooling, prompt libraries, research workspace/browser, character workflows, document ingestion, local storage/privacy tooling, theme systems, diagnostics, and Windows/macOS/Linux packaging paths.

Tech stack

Electron 42
React 19
Vite 6
TypeScript
Zustand
Vitest
Express proxy/server
electron-builder
IndexedDB/local persistence
Electron safeStorage / desktop bridge
Venice API
Jina/research integrations

Maturity assessment

The repo is much healthier than a normal “AI desktop app with gradients and vibes” situation. It has strong scripts, security docs, packaging config, CI, CodeQL, dependency review, platform smoke tests, document ingestion tests, and a pile of verifier scripts.

But two verified problems are release-blocking or production-risky:

  1. Tab switching unmounts Chat and aborts active streams.
  2. The release hardening verifier fails in ZIP/archive mode after local config files are generated.

Biggest strengths

  • npm ci passes.
  • npm run lint passes.
  • npm run typecheck passes.
  • npm run build passes.
  • 448 test files exist.
  • CI covers Ubuntu, Windows, macOS, Electron smoke tests, audit, coverage, build, and verifier gates.
  • Electron security defaults are mostly sane: contextIsolation: true, nodeIntegration: false, sandbox: true, webSecurity: true.
  • Markdown/LaTeX rendering uses react-markdown, remark-math, rehype-katex, and rehype-sanitize.
  • Character image cache is bounded, TTL-based, allowlisted, content-type checked, magic-byte checked, and isolated under app cache.

Biggest weaknesses

  • Long-running stream ownership is tied to React hook/component lifetime.
  • Archive-mode release validation is not idempotent after local validation creates .config/*.local.yaml.
  • Document ingestion wrappers escape attributes but not file body text, so user-uploaded content can break out of the wrapper.
  • Legacy .doc files can be accepted but silently send no extracted text.
  • Spreadsheet classification supports .xls/.xlsx, but the upload UI does not advertise them and the ingestion route treats them as text.
  • Some verifier scripts are token/presence checks and miss semantic bugs. Humanity invented tests, then reinvented grep and called it validation.

Immediate risks

P0: Active chat stream aborts on tab switch or component unmount.
P0: Release packaging hardening fails in no-.git ZIP/archive mode after config generation.
P1: Attachment body delimiter injection can manipulate model context.
P1: Legacy DOC and spreadsheet ingestion behavior can mislead users.
P1: IPC duplicate-handler regression coverage is incomplete for minimize/restore/window recreation.

Runnable/buildable/testable/maintainable assessment

Area Result
npm ci Pass
npm run lint Pass
npm run typecheck Pass
npm run build Pass
npm test -- --run Timed out after 300s; targeted failure found
npm run test:coverage -- --run Timed out after 300s; same targeted failure
npm run verify:release-packaging-hardening Fail after .config/*.local.yaml generation
.git metadata in uploaded ZIP Missing, expected for ZIP snapshot

2. Repository Evidence Map

Verified entry points

package.json:10
  main: dist-electron/electron/main.js

electron/main.ts:97-174
  BrowserWindow creation and main renderer load

electron/preload.ts
  contextBridge desktop API exposure

src/App.tsx:238-264
  Renderer app shell and active view rendering

server.ts:163+
  Express local/dev server

electron/ipc/handlers.ts:213+
  Main desktop IPC handlers

src/services/desktopBridge.ts
  Renderer-side desktop bridge client

electron/services/veniceClient.ts
  Main-process Venice request/stream client

electron/services/researchBrowserServer.ts:92+
  Research browser IPC and WebContentsView lifecycle

Verified source directories

src/components/
src/hooks/
src/services/
src/stores/
src/shared/
src/types/
src/theme/
electron/
electron/ipc/
electron/services/
scripts/
tests/
docs/
config/
.config/

Verified build/config files

package.json
package-lock.json
vite.config.ts
vitest.config.ts
tsconfig.json
tsconfig.electron.json
eslint.config.mjs
electron-builder.config.cjs
.nvmrc
.env.example
.config/config.example.yaml
.config/themes.example.yaml

Verified test files

448 files matching *.test.ts / *.test.tsx
tests/smoke/electron-smoke.test.ts
electron/services/researchBrowserServer.test.ts
electron/services/characterImageCache.test.ts
src/hooks/use-chat.test.ts
src/services/ingestion/*.test.ts
scripts/verify-release-packaging-hardening.test.ts

Verified CI/CD workflows

.github/workflows/ci.yml
.github/workflows/release.yml
.github/workflows/codeql.yml
.github/workflows/dependency-review.yml
.github/dependabot.yml

Verified documentation files

README.md
SECURITY.md
PRIVACY.md
LEGAL.md
SUPPORT.md
docs/DOCS_INDEX.md
docs/design/REPOSITORY_TREE.md
docs/legal/*
docs/RELEASE/*
docs/DEVELOPMENT/*
docs/audits/*
docs/reports/historical/*

Verified release/package files

electron-builder.config.cjs
scripts/verify-dist.cjs
scripts/verify-release-packaging-hardening.cjs
scripts/verify-archive-clean.cjs
scripts/checksum-release.cjs
build/icon.ico
build/icon.icns
build/icon.png

Verified security-sensitive files

electron/main.ts
electron/preload.ts
electron/ipc/handlers.ts
electron/services/secureStore.ts
electron/services/configService.ts
electron/services/veniceClient.ts
electron/services/characterImageCache.ts
src/services/desktopBridge.ts
src/services/ingestion/*
src/shared/redaction.ts
src/components/chat/message-bubble.tsx
server.ts

Verified generated/stale/report files

docs/audits/*
docs/archives/*
docs/reports/historical/*
docs/summary_of_work.md
docs/BUG_HUNTING_AGENT_PROMPT.md
_REPO_EXTRACT_METADATA/*

Missing or recommended standard repo files

Most standard files exist. Recommended additions are issue-specific:

src/stores/chat-stream-manager.ts (new)
src/stores/chat-stream-manager.test.ts (new)
src/services/ingestion/xmlEscape.test.ts (new or expanded)
docs/reports/CANONICAL_REPORT_INDEX.md (new)
scripts/verify-source-archive-clean.cjs (new or fold into existing verifier)

3. Critical Findings

CF-001: Tab switching aborts active chat streams

  • Evidence: src/App.tsx:257-259 keys the active tab view by normalisedActiveTab, forcing unmount/remount on tab switch. src/components/chat/chat-view.tsx:46 owns useChat(). src/hooks/use-chat.ts:395-409 creates a local AbortController per stream. src/hooks/use-chat.ts:470-479 aborts in-flight streams on hook unmount. src/hooks/use-chat.test.ts:327-353 explicitly tests this abort-on-unmount behavior.
  • Impact: Changing tabs can terminate active provider streams. That matches the reported behavior and can waste requests or worsen rate-limit failures.
  • Priority: P0
  • Affected files: src/App.tsx, src/hooks/use-chat.ts, src/hooks/use-chat.test.ts, likely src/stores/chat-store.ts, new stream manager/store.
  • Recommended fix: Move provider stream lifetime into a store/service that survives tab/component unmount. UI should subscribe/unsubscribe; only explicit Stop should abort.
  • Validation: Add regression tests proving tab switch/unmount does not abort, while Stop still aborts.

CF-002: Release hardening fails in ZIP/archive mode after local config generation

  • Evidence: The uploaded ZIP has no .git, so git status, git branch, and git rev-parse fail. electron/services/configService.ts:172-179 resolves repo-local config paths under .config/config.local.yaml and .config/themes.local.yaml; electron/services/configService.ts:333-349 creates them if missing. scripts/verify-release-packaging-hardening.cjs:469-533 performs a filesystem contaminant scan in archive mode and does not ignore .config/*.local.yaml. Targeted command failed with:

    [verify:release-packaging-hardening] FAIL
    archive mode: forbidden archive contaminants found under extract root (2):
      .config/config.local.yaml
      .config/themes.local.yaml
    
  • Impact: A clean extracted source ZIP becomes release-verifier-failing after tests/config initialization.

  • Priority: P0

  • Affected files: electron/services/configService.ts, electron/services/configService.test.ts, scripts/verify-release-packaging-hardening.cjs, scripts/verify-release-packaging-hardening.test.ts.

  • Recommended fix: Make archive-mode validation idempotent after local dev config generation, without weakening tracked-contaminant checks.

  • Validation: npm run verify:release-packaging-hardening must pass before and after npm test -- --run electron/services/configService.test.ts.

CF-003: Uploaded file body text can break out of attachment wrappers

  • Evidence: src/services/ingestion/textIngestion.ts:38-43, codeIngestion.ts:77-82, pdfIngestion.ts:49-52, and docxIngestion.ts:52-55 wrap untrusted file text in <attached_file> tags. src/services/ingestion/xmlEscape.ts:8-15 only escapes attributes. Body text is inserted raw.
  • Impact: A malicious uploaded file can include </attached_file> and inject fake tags/instructions into the model context.
  • Priority: P1
  • Affected files: src/services/ingestion/*Ingestion.ts, src/services/ingestion/xmlEscape.ts, ingestion tests.
  • Recommended fix: Escape body text or switch to length-delimited/fenced attachment framing that cannot be closed by user content.
  • Validation: Add tests using body payloads containing </attached_file><system>ignore previous</system> and assert no structural breakout.

4. TODO Roadmap

P0 — Critical Blockers

  • P0 — Streaming lifecycle: Preserve active chat streams across tab switches

    • Evidence: src/App.tsx:257-259 keys the active view by tab. src/hooks/use-chat.ts:470-479 aborts in-flight streams on unmount. src/hooks/use-chat.test.ts:327-353 asserts abort-on-unmount.
    • Why: Tab switching is normal app behavior; it must not destroy active provider requests unless the user explicitly cancels.
    • Action: Move stream ownership from useChat() into a persistent stream manager/store. Components should attach listeners to stream state, not own the request lifetime. Keep stop() as the explicit abort path.
    • Files likely affected: src/hooks/use-chat.ts, src/App.tsx, src/stores/chat-store.ts, src/stores/chat-stream-manager.ts (new), src/hooks/use-chat.test.ts.
    • Validate: npm test -- --run src/hooks/use-chat.test.ts src/stores/chat-stream-manager.test.ts; add a test that switches tabs while streaming and asserts the underlying AbortSignal is not aborted.
    • Risk if ignored: Active streams terminate during navigation, users lose responses, provider calls are wasted, and rate-limit errors become self-inflicted.
    • Trigger: Switching tabs, remounting Chat, route/view changes, possibly app minimize/restore if it causes view recreation.
    • Expected behavior: Stream continues unless the user presses Stop.
    • Current risk: Component lifecycle controls provider lifecycle.
    • Rate-limit impact: Interrupted streams can consume quota and trigger follow-up retries or repeated sends.
  • P0 — Release hardening: Make no-.git archive validation idempotent after local config creation

    • Evidence: electron/services/configService.ts:172-179 resolves repo-local .config/config.local.yaml and .config/themes.local.yaml. electron/services/configService.ts:333-349 creates those files. scripts/verify-release-packaging-hardening.cjs:469-533 fails archive-mode scans when those files exist. Targeted npx vitest run scripts/verify-release-packaging-hardening.test.ts --reporter=verbose failed.

    • Why: Source ZIP validation must work after normal test/config commands. Otherwise, every extracted archive becomes a tiny release-gate landmine.

    • Action: Either force config tests to use temp config paths via VENICE_FORGE_CONFIG_FILE / VENICE_FORGE_THEMES_FILE, or teach archive-mode verifier to distinguish generated local-only config from source contaminants. Keep strict git-tracked checks unchanged.

    • Files likely affected: electron/services/configService.test.ts, scripts/verify-release-packaging-hardening.cjs, scripts/verify-release-packaging-hardening.test.ts.

    • Validate:

      rm -f .config/config.local.yaml .config/themes.local.yaml
      npm run verify:release-packaging-hardening
      npm test -- --run electron/services/configService.test.ts scripts/verify-release-packaging-hardening.test.ts
      npm run verify:release-packaging-hardening
    • Risk if ignored: Release/archive validation fails after routine local validation, causing false blockers and confusing agents into “fixing” the wrong thing.

    • Platform: Both, plus source ZIP/archive mode.

    • Expected behavior: Local generated config stays ignored/untracked and does not poison archive validation.

    • Current risk: No-.git archive mode treats generated local config as forbidden source content.


P1 — Production Readiness

  • P1 — Security: Prevent attachment-wrapper delimiter injection

    • Evidence: src/services/ingestion/textIngestion.ts:40-43, codeIngestion.ts:79-82, pdfIngestion.ts:49-52, and docxIngestion.ts:52-55 insert raw user file text inside XML-like wrappers. src/services/ingestion/xmlEscape.ts:8-15 only escapes attributes.

    • Why: User-uploaded files are untrusted. Raw body text can close the wrapper and inject fake model instructions.

    • Action: Add escapeXmlText() or replace wrappers with length-delimited fenced blocks:

      <attached_file ... encoding="text/plain" length="1234">
      <![CDATA[...escaped or length-delimited content...]]>
      </attached_file>
      

      Prefer a framing format that preserves code formatting and cannot be closed by user text.

    • Files likely affected: src/services/ingestion/xmlEscape.ts, textIngestion.ts, codeIngestion.ts, pdfIngestion.ts, docxIngestion.ts, matching tests.

    • Validate: npm test -- --run src/services/ingestion/textIngestion.test.ts src/services/ingestion/codeIngestion.test.ts src/services/ingestion/pdfIngestion.test.ts src/services/ingestion/docxIngestion.test.ts

    • Risk if ignored: Malicious attachments can manipulate prompt structure and defeat the intended “reference-only” boundary.

    • Threat: Prompt-injection document closes the attachment tag and adds fake privileged instructions.

    • Affected surface: Renderer ingestion, chat payload assembly, research upload ingestion.

    • Residual risk: Model-level instruction-following risk remains, but structural wrapper escape is blocked.

    • Status: Fixed 2026-06-21. escapeXmlText() now escapes local text/code/PDF/DOCX wrapper bodies, and malicious-body regression tests plus npm run verify:document-ingestion pass.

  • P1 — Document ingestion: Do not silently send accepted .doc files with no extracted text

    • Evidence: src/components/chat/chat-input.tsx:19-22 accepts .doc. src/services/ingestion/docxIngestion.ts:98-124 returns a legacy DOC attachment with no text. src/hooks/use-chat.ts:362-369 only appends att.text or image data, so .doc content is omitted from the actual message.
    • Why: The UI can imply the file is attached while the model receives none of its contents. That is user-hostile in the way only file upload UX can be.
    • Action: Block send for .doc until parsed, or implement the advertised “Parse with Venice” / approved converter path. Show a blocking toast if no extracted text exists.
    • Files likely affected: src/services/ingestion/docxIngestion.ts, src/components/chat/chat-input.tsx, src/hooks/use-chat.ts, src/services/ingestion/attachmentAssembler.test.ts, src/components/chat/chat-view.test.tsx.
    • Validate: Add a test that uploads .doc, attempts send, and asserts either a parse workflow occurs or send is blocked with a visible warning.
    • Risk if ignored: Users believe a document was sent, but the model receives only the typed message.
    • Platform: Both desktop and web/dev mode.
    • Expected behavior: Unsupported legacy documents are clearly blocked or parsed before send.
    • Current risk: Accepted attachment can be semantically empty.
    • Status: Fixed 2026-06-21. .doc is no longer classified as supported, no longer appears in the ChatInput accept list, and assembler/DOC tests assert legacy .doc files reject with UnsupportedFileTypeError until a real parser is available.
  • P1 — Document ingestion: Resolve spreadsheet support drift

    • Evidence: src/services/ingestion/fileClassifier.ts:88 classifies .csv, .xlsx, and .xls as spreadsheet. src/services/ingestion/attachmentAssembler.ts:18-21 routes spreadsheet to ingestTextFile. src/components/chat/chat-input.tsx:19-83 accepts .csv but does not include .xlsx or .xls.
    • Why: Binary Excel files routed through text ingestion become garbage or fail unpredictably.
    • Action: Either remove .xlsx/.xls classification until supported, or add real spreadsheet parsing and update the accept list, tests, docs, and extraction metadata.
    • Files likely affected: src/services/ingestion/fileClassifier.ts, attachmentAssembler.ts, textIngestion.ts, chat-input.tsx, ingestion tests, docs/design/REPOSITORY_TREE.md.
    • Validate: npm test -- --run src/services/ingestion/fileClassifier.test.ts src/services/ingestion/attachmentAssembler.test.ts
    • Risk if ignored: Excel attachments produce misleading or corrupted model context.
    • Status: Fixed 2026-06-21. .csv remains supported as text-ingested spreadsheet data; binary .xls/.xlsx are no longer classified as supported and assembler tests assert they are rejected instead of routed through text ingestion.
  • P1 — Electron IPC: Add idempotency guard and regression coverage for main handler registration

    • Evidence: electron/ipc/handlers.ts:213-219 registers handlers directly with ipcMain.handle. electron/main.ts:177-254 currently calls registerIpcHandlers() from bootstrap, so normal boot is likely safe. Research browser IPC has its own guard in electron/services/researchBrowserServer.ts:86-98. The observed prior crash was duplicate researchBrowser:create, but current tests at electron/services/researchBrowserServer.test.ts:130-149 only assert channel registration, not duplicate setup/minimize/restore.
    • Why: Electron throws if a handler is registered twice. That exact class of bug has already appeared after minimize/restore.
    • Action: Add explicit idempotency to registerIpcHandlers(), and add tests for repeated setupResearchBrowserIpc() and repeated registerIpcHandlers() calls.
    • Files likely affected: electron/ipc/handlers.ts, electron/ipc/handlers.test.ts, electron/services/researchBrowserServer.test.ts.
    • Validate: npm test -- --run electron/ipc/handlers.test.ts electron/services/researchBrowserServer.test.ts
    • Risk if ignored: Window recreation or future bootstrap refactors can reintroduce duplicate-handler crashes.
    • Platform: macOS primarily for activate/minimize flows; both for second-window/lifecycle defects.
    • Expected behavior: Handler setup is safe to call more than once.
    • Current risk: Research browser has a guard; general IPC registration does not.
    • Status: Fixed 2026-06-21. registerIpcHandlers() and registerUpdateHandlers() now return early after first registration; electron/ipc/handlers.test.ts proves repeated registration does not call ipcMain.handle, and research-browser recreate/idempotency coverage passes.
  • P1 — Testing reliability: Fix full test/coverage timeout and warning noise

    • Evidence: npm test -- --run timed out after 300 seconds. npm run test:coverage -- --run also timed out. During execution, Vitest emitted MaxListenersExceededWarning from repeated server setup. server.ts:173-175 attaches exit, SIGINT, and SIGTERM listeners every time createServerApp() is called.

    • Why: CI may pass in one environment while local/source-ZIP validation becomes unreliable. Flaky test gates are just roulette with YAML.

    • Action: Ensure createServerApp() does not repeatedly register global process listeners in tests, or expose cleanup/dispose hooks. Fix React act(...) warnings in chat hook tests.

    • Files likely affected: server.ts, server.test.ts, src/hooks/use-chat.test.ts.

    • Validate:

      npm test -- --run
      npm run test:coverage
    • Risk if ignored: Full test runs remain hard to trust, timeout-prone, and noisy.

    • Status: Fixed 2026-06-21. createServerApp() cleanup now removes per-app shutdown listeners, server.test.ts asserts listener counts return to baseline, npm test -- --run passes in 155.03s, and npm run test:coverage passes in 177.78s with coverage thresholds met.

  • P1 — Desktop attachment parity: Expand or deprecate app:readLocalFile

    • Evidence: Renderer upload supports PDF, DOCX, DOC, Markdown, JSON/YAML, code, and images in src/components/chat/chat-input.tsx:19-83. Desktop IPC app:readLocalFile only allows txt, md, json, csv, yaml, and yml in electron/ipc/handlers.ts:829-846.
    • Why: Desktop file picker behavior can diverge from drag/drop or web upload behavior.
    • Action: Either route desktop file selection through the same renderer ingestion pipeline, or rename/scope app:readLocalFile as text-only and avoid presenting it as general attachment import.
    • Files likely affected: electron/ipc/handlers.ts, electron/preload.ts, src/services/desktopBridge.ts, src/components/chat/chat-input.tsx.
    • Validate: Add tests for desktop file picker accepted extensions and renderer accepted extensions matching intentionally.
    • Risk if ignored: Users get inconsistent file support depending on how they attach files.
    • Platform: Desktop only.
    • Expected behavior: One clear attachment capability matrix.
    • Current risk: Native dialog is narrower than renderer ingestion.
    • Status: Fixed 2026-06-21 by explicitly scoping app:readLocalFile as a text-only native picker. The dialog title/filter advertise text attachments only, attachmentService.readLocalPathAttachment() is text-only, and IPC tests assert PDF/DOCX/DOC/binary spreadsheet/image formats are rejected instead of presented as general attachment import.
  • P1 — Memory UX: Make auto-injected memory auditable on every sent message

    • Evidence: src/hooks/use-chat.ts:291-357 can auto-pull and inject memory when showPulledContextBeforeSending is false. Preview UI exists at src/components/chat/chat-view.tsx:289-325, but auto mode bypasses preview and stores injected context in message metadata.
    • Why: Memory/context injection must be visible, scoped, and reversible. Silent contextual recall is where “helpful” becomes “why did it know that,” which is a fun legal and UX swamp.
    • Action: Add a per-message context disclosure pill/inspector for auto-injected memory, and tests proving archived/deleted/out-of-project memory is not injected.
    • Files likely affected: src/hooks/use-chat.ts, src/components/chat/message-bubble.tsx, src/components/layout/memory-panel.tsx, src/services/memoryService.ts, relevant tests.
    • Validate: Add tests for global off, conversation off, preview on, preview off, character-bound chat disabled, and project scoping.
    • Risk if ignored: Users may not understand or control what memory influenced a response.
    • Status: Fixed 2026-06-21 for the per-message audit surface. useChat() already stores injected context and source in sent-message metadata; MessageBubble now renders a collapsible disclosure for that metadata on user and assistant messages, with tests proving memory context is visible and ordinary messages remain undisclosed.
  • P1 — Source/archive naming: Resolve Windows-Venice-API-connector vs Venice_Forge drift

    • Evidence: Uploaded ZIP/root name is Windows-Venice-API-connector-clean-20260621-070025.zip. package.json:1-18 declares venice-forge and GitHub URLs under spearchucker667/Venice_Forge. electron-builder.config.cjs:45-49 publishes to Venice_Forge.
    • Why: Artifact naming, repo URLs, docs, and support links should not disagree.
    • Action: Pick one canonical public slug. Update zip/export scripts, README snippets, release artifact docs, and verifier checks to flag retired repo names.
    • Files likely affected: package.json, README.md, docs/*, source ZIP script, scripts/verify-release-packaging-hardening.cjs.
    • Validate: Add verifier check that source archives and docs do not contain retired repository names except in historical archives.
    • Risk if ignored: Users and contributors file bugs against the wrong repo or install the wrong artifact.
    • Platform: Repo/distribution.
    • Expected behavior: Current app, package, artifact, and repo names align.
    • Current risk: Public metadata split-brain.
    • Status: Verified 2026-06-21. Active package metadata, README, CONTRIBUTING, SECURITY, release docs, workflows, and electron-builder publish config use the canonical spearchucker667/Venice_Forge slug. verify-release-packaging-hardening includes a retired-slug guard for active release/setup metadata; remaining old names are historical audit/handoff evidence or redaction/verifier fixtures.
  • P1 — Verifier quality: Strengthen semantic checks for document ingestion

    • Evidence: scripts/verify-document-ingestion.cjs:75-129 checks token presence such as SUPPORTED_ATTACHMENT_ACCEPT, AI is not vision capable, and rehypeSanitize, then runs tests. It does not check attachment body escaping, .doc no-text behavior, or .xlsx/.xls routing.
    • Why: Token checks can pass while the behavior remains broken.
    • Action: Add explicit semantic regression tests for the ingestion edge cases above and make the verifier call those tests.
    • Files likely affected: scripts/verify-document-ingestion.cjs, src/services/ingestion/*.test.ts, src/components/chat/chat-view.test.tsx.
    • Validate: npm run verify:document-ingestion
    • Risk if ignored: Validation claims ingestion is safe while wrapper breakout and no-content sends remain possible.
    • Status: Fixed 2026-06-21. The verifier now runs semantic ingestion regression files covering body escaping, .doc rejection, and .xls/.xlsx rejection; npm run verify:document-ingestion passes.

P2 — Quality, DX, and Maintainability

  • P2 — Repo hygiene: Canonicalize audit/report sprawl

    • Evidence: docs/audits/*, docs/archives/*, docs/reports/historical/*, and docs/summary_of_work.md exist. docs/DOCS_INDEX.md already separates current and historical docs.
    • Why: Too many report files make contributors chase stale advice like raccoons in a data center.
    • Action: Create one canonical report index mapping old reports to current documents; move superseded files under docs/archives/ only after preserving release evidence.
    • Files likely affected: docs/DOCS_INDEX.md, docs/reports/README.md, docs/reports/CANONICAL_REPORT_INDEX.md (new), docs/archives/*, docs/audits/*.
    • Validate: npm run verify:markdown-links && npm run verify:repo-handoff-hygiene
    • Risk if ignored: Agents and contributors follow stale TODOs.
    • Status: Fixed 2026-06-21. Added docs/reports/CANONICAL_REPORT_INDEX.md, linked it from docs/DOCS_INDEX.md and docs/reports/README.md, and moved the completed VENICE_FORGE_TODO.md / VENICE_FORGE_ZIP_AUDIT_HANDOFF.md handoffs into docs/archives/ as historical evidence.
  • P2 — Supply chain: Track deprecated transitive packages

    • Evidence: npm ci passed with zero vulnerabilities but emitted deprecated warnings for packages including lodash.isequal, inflight, glob@7, boolean, and rimraf@2.
    • Why: Deprecated transitive dependencies can become future audit or install blockers.
    • Action: Run npm explain for each deprecated package, update direct dependencies where possible, and document unavoidable transitive debt.
    • Files likely affected: package.json, package-lock.json, docs/DEVELOPMENT/troubleshooting.md.
    • Validate: npm ci && npm audit --audit-level=moderate
    • Risk if ignored: Future Node/npm releases may turn warnings into breakage.
    • Status: Tracked 2026-06-21. npm explain lodash.isequal inflight glob boolean rimraf identifies these as Electron packaging/updater transitives, and docs/DEVELOPMENT/troubleshooting.md records the current parent paths plus the npm audit --audit-level=moderate zero-vulnerability result.
  • P2 — Performance: Keep bundle budget focused on heavy PDF/media chunks

    • Evidence: npm run build passed, but Vite emitted large chunks including a PDF worker around 1.37 MB and main JS chunks around 500–576 KB. package.json:61 already has verify:bundle-budget.
    • Why: Document ingestion is valuable, but PDF workers should stay lazily loaded and budgeted.
    • Action: Confirm PDF/docx/media modules are code-split and that verify:bundle-budget fails on regressions.
    • Files likely affected: vite.config.ts, scripts/verify-bundle-budget.cjs, ingestion services/components.
    • Validate: npm run build && npm run verify:bundle-budget
    • Risk if ignored: Startup gets heavy, especially on Windows machines already suffering enough.
    • Status: Verified 2026-06-21. npm run build and npm run verify:bundle-budget pass; PDF/media chunk budgets remain enforced by the contract script.
  • P2 — Architecture: Split oversized core files

    • Evidence: Large files include src/services/veniceClient.ts at 1,586 lines, electron/ipc/handlers.ts at 1,331 lines, src/theme/themes.ts at 1,113 lines, and src/components/SettingsView.tsx at 1,009 lines.
    • Why: Huge files make regression review harder and increase merge conflict risk.
    • Action: Split by responsibility: Venice request validation/streaming/errors, IPC domains, theme registry/schema, settings sections.
    • Files likely affected: src/services/veniceClient.ts, electron/ipc/handlers.ts, src/theme/themes.ts, src/components/SettingsView.tsx.
    • Validate: npm run typecheck && npm test -- --run electron/ipc/handlers.test.ts src/services/veniceClient.test.ts
    • Risk if ignored: Future feature additions keep landing in mega-files.
    • Status: Fixed 2026-06-21. src/theme/themes.ts is now a barrel over src/theme/builtins/*.ts; src/components/SettingsView.tsx is a barrel over src/components/settings/*.tsx; electron/ipc/handlers.ts is a barrel over electron/ipc/handlers/*.ts; src/services/veniceClient.ts is a barrel over src/services/veniceClient/*.ts. All original import paths remain valid, all tests pass, and verify:contracts passes.
  • P2 — Character image cache: Add concurrent fetch de-duplication

    • Evidence: electron/services/characterImageCache.ts:291-377 checks cache, then fetches and writes. Tests cover cache hit, TTL, stale fallback, size/type/magic checks in electron/services/characterImageCache.test.ts:90-258, but no pending-request de-duplication is visible.
    • Why: Opening a character grid can request the same avatar multiple times in parallel before the first write completes.
    • Action: Add an in-flight promise map by cache key so concurrent calls share one upstream fetch.
    • Files likely affected: electron/services/characterImageCache.ts, electron/services/characterImageCache.test.ts.
    • Validate: Add test with two simultaneous getCachedCharacterImage(url) calls and assert fetch runs once.
    • Risk if ignored: Extra upstream calls and slower character image loading.
    • Status: Fixed 2026-06-21. Cache misses now share an in-flight promise by cache key, and electron/services/characterImageCache.test.ts asserts concurrent calls fetch once and return the same cached URL.
  • P2 — Markdown safety: Consolidate dual Markdown renderers

    • Evidence: Chat messages use ReactMarkdown with rehypeSanitize in src/components/chat/message-bubble.tsx:281-293. A separate fallback renderer in src/utils/markdown.tsx:69-113 uses dangerouslySetInnerHTML after custom sanitization.
    • Why: Two renderers mean two security surfaces and inconsistent Markdown behavior.
    • Action: Prefer one hardened Markdown/LaTeX renderer or document why both exist. Add XSS regression fixtures to both if retained.
    • Files likely affected: src/components/chat/message-bubble.tsx, src/utils/markdown.tsx, tests.
    • Validate: npm test -- --run src/components/chat/message-bubble.test.tsx
    • Risk if ignored: Future rendering fixes land in one path and miss the other.
    • Status: Fixed 2026-06-21 by retaining both renderers with explicit XSS regression coverage. src/utils/markdown.test.ts covers the fallback dangerouslySetInnerHTML sanitizer, and src/components/chat/message-bubble.test.tsx now covers chat ReactMarkdown sanitization of raw HTML, event handlers, and javascript: URLs.
  • P2 — Theme/UX: Keep mesh visual system token-driven

    • Evidence: App shell uses AppMeshOverlay and mesh-panel in src/App.tsx:238-257; package.json:68 includes verify:theme-tokens; README documents theme-driven UI and smooth visual direction.
    • Why: The project explicitly wants smooth mesh visuals, but hardcoded styles can creep back in.
    • Action: Expand verify:theme-tokens to scan new component areas and add visual regression snapshots for Chat, Research, Media Studio, Settings, and light theme input text.
    • Files likely affected: scripts/verify-theme-tokens.cjs, src/components/**, docs/design/THEME_SYSTEM.md.
    • Validate: npm run verify:theme-tokens && npm test -- --run tests/theme
    • Risk if ignored: The app returns to hard-line panel soup.
    • Status: Verified 2026-06-21. npm run verify:theme-tokens scans 100 themeable UI files with no forbidden hardcoded color classes, and theme-focused Vitest coverage passes across tests/theme, src/theme, and ThemeMaker tests.

P3 — Future Enhancements

  • P3 — Roadmap: Add stream resume metadata for recoverable provider drops

    • Evidence: Current stream support includes abort IDs in electron/ipc/handlers.ts:257-309 and src/services/desktopBridge.ts, but no verified resume protocol.
    • Why: Once stream lifetime survives tab switching, resumability becomes the next reliability layer.
    • Action: Track provider request IDs where available, partial content checkpoints, and retry-safe error categories.
    • Files likely affected: src/stores/chat-stream-manager.ts (new), electron/services/veniceClient.ts, diagnostics services.
    • Validate: Simulated network drop test with no duplicate user message.
    • Risk if ignored: Drops remain terminal.
  • P3 — Roadmap: Add richer file-ingestion adapters

    • Evidence: Current ingestion supports PDF/DOCX/text/code/image paths, but .doc and spreadsheet flows need hardening.
    • Why: Real users upload messy files because apparently file formats were designed by rival kingdoms.
    • Action: Add spreadsheet parsing, OCR queue, and provider-capability-aware extraction routing.
    • Files likely affected: src/services/ingestion/*, docs, tests.
    • Validate: Fixture matrix for PDF, scanned PDF, DOCX, DOC, CSV, XLSX, images, code files.
    • Risk if ignored: Advanced ingestion remains uneven.
    • Status: Fixed 2026-07-01. Added support for legacy .doc and .xls/.xlsx spreadsheet files via Venice API's text parsing fallback. Automatically routes image files to OCR extraction when using models without vision capabilities. Test coverage fully maps all pathways.

5. Category Coverage Matrix

Category Status Evidence inspected Notes
Build/runtime Covered package.json, npm ci, lint, typecheck, build Build passes; tests timeout/fail after local config generation
Architecture Covered top file sizes, src/App.tsx, use-chat.ts, IPC/services Stream lifecycle needs ownership refactor
Security Covered Electron main/preload/IPC, ingestion, Markdown, config, secret scan Main Electron defaults strong; ingestion wrapper needs hardening
Testing Covered 448 test files, Vitest runs, targeted failure Full suite timed out locally
CI/CD Covered .github/workflows/ci.yml, release.yml CI is strong and includes platform smoke tests
Documentation Covered README, docs index, repo tree, legal/security docs Docs are extensive; report sprawl needs indexing
Developer experience Covered scripts, .nvmrc, docs, config examples Strong script surface; archive-mode idempotence weak
Dependencies Covered npm ci, package.json, lockfile Zero audit vulns; deprecated transitive warnings
Packaging/release Covered electron-builder, release workflow, verify scripts Release verifier fails in generated-config archive mode
Config/env Covered .env.example, .config, configService.ts Secure write path strips secrets; local file generation trips verifier
Logging/diagnostics Covered electron/main.ts, server logging, redaction paths Console logs are redacted; tests still print fixture tokens in warnings
Performance/reliability Covered stream lifecycle, bundle output, character cache Stream lifetime is top reliability issue
UX/app behavior/accessibility Covered App shell, chat input, memory preview, themes Need visual regression around theme/mesh reports
GitHub hygiene Covered templates, CODEOWNERS, workflows, docs/reports Good baseline; report sprawl remains
Legal/licensing/privacy Covered LICENSE, LEGAL, SECURITY, PRIVACY, docs/legal Unofficial-client and trademark language present
Roadmap Covered P3 items Future work should follow stabilization

6. Suggested GitHub Issues

P0 Issues

  1. [P0] Preserve active chat streams across tab switches and Chat unmounts
  2. [P0] Fix release hardening failure caused by generated .config/*.local.yaml in archive mode

P1 Issues

  1. [P1] Escape or length-delimit uploaded attachment body text
  2. [P1] Block or parse legacy .doc attachments before send
  3. [P1] Resolve spreadsheet .xls/.xlsx classification and ingestion drift
  4. [P1] Add IPC idempotency regression tests for minimize/restore/window recreation
  5. [P1] Fix full Vitest/coverage timeout and MaxListeners warnings
  6. [P1] Align desktop native file picker with renderer attachment support
  7. [P1] Make auto-injected memory visible and project-scoped
  8. [P1] Resolve Windows-Venice-API-connector vs Venice_Forge naming drift
  9. [P1] Strengthen semantic verifier checks for document ingestion

P2 Issues

  1. [P2] Canonicalize docs/audits and docs/reports historical sprawl
  2. [P2] Track and reduce deprecated transitive npm dependencies
  3. [P2] Verify PDF/media lazy loading and bundle budget
  4. [P2] Split oversized IPC, Venice client, theme, and settings files
  5. [P2] Add concurrent fetch de-duplication to character image cache
  6. [P2] Consolidate or test dual Markdown renderers
  7. [P2] Expand theme-token and visual regression coverage

P3 Issues

  1. [P3] Add stream resume metadata for recoverable provider drops
  2. [P3] Add richer file-ingestion adapters for OCR and spreadsheets

7. Suggested Milestones

0.1.0 — Repo Stabilization

Included TODOs:

[P0] Fix release hardening failure caused by generated .config/*.local.yaml
[P1] Fix full Vitest/coverage timeout and MaxListeners warnings
[P1] Resolve source/archive naming drift

0.2.0 — Test and CI Foundation

Included TODOs:

[P1] Add IPC idempotency regression tests
[P1] Strengthen semantic verifier checks for document ingestion
[P2] Expand theme-token and visual regression coverage

0.3.0 — Security, Privacy, and Config Hardening

Included TODOs:

[P1] Escape or length-delimit uploaded attachment body text
[P1] Make auto-injected memory visible and project-scoped
[P2] Consolidate or test dual Markdown renderers

0.4.0 — Windows/macOS Packaging and Release Pipeline

Included TODOs:

[P0] Fix archive-mode release hardening
[P1] Resolve naming drift
[P2] Track deprecated dependencies

0.5.0 — UX, Accessibility, and Diagnostics Hardening

Included TODOs:

[P0] Preserve streams across tab switches
[P1] Align desktop native file picker with renderer attachment support
[P2] Expand visual regression coverage

1.0.0 — Production-Ready Desktop Release

Included TODOs:

All P0 and P1 items closed
Full test/coverage passes locally and in CI
Windows/macOS packaged smoke tests pass
Release artifacts checksummed and signing status explicit

2.0.0 — Advanced Venice Forge Platform Roadmap

Included TODOs:

[P3] Stream resume metadata
[P3] OCR/spreadsheet adapters
Advanced diagnostics and recovery UX

8. Recommended First 10 Actions

  1. Fix stream lifetime ownership

    • Command: npm test -- --run src/hooks/use-chat.test.ts
    • Files: src/hooks/use-chat.ts, src/App.tsx, new src/stores/chat-stream-manager.ts
    • Expected outcome: Tab switch no longer aborts active stream.
    • Validation: New tab-switch regression test.
  2. Patch release verifier/config-test interaction

    • Command: npm run verify:release-packaging-hardening
    • Files: electron/services/configService.test.ts, scripts/verify-release-packaging-hardening.cjs
    • Expected outcome: Verifier passes in no-.git ZIP mode before and after config tests.
    • Validation: Run verifier before and after targeted tests.
  3. Add attachment body escaping

    • Command: npm test -- --run src/services/ingestion/textIngestion.test.ts src/services/ingestion/codeIngestion.test.ts
    • Files: src/services/ingestion/xmlEscape.ts, ingestion services.
    • Expected outcome: Uploaded file content cannot close wrapper tags.
    • Validation: Malicious body fixture tests.
  4. Block empty .doc sends

    • Command: npm test -- --run src/services/ingestion/docxIngestion.test.ts src/components/chat/chat-view.test.tsx
    • Files: docxIngestion.ts, chat-input.tsx, use-chat.ts
    • Expected outcome: Legacy DOC either parsed or blocked.
    • Validation: .doc send test.
  5. Resolve spreadsheet support

    • Command: npm test -- --run src/services/ingestion/fileClassifier.test.ts src/services/ingestion/attachmentAssembler.test.ts
    • Files: fileClassifier.ts, attachmentAssembler.ts, chat-input.tsx
    • Expected outcome: .xls/.xlsx behavior is explicit and tested.
    • Validation: Fixture matrix.
  6. Add IPC idempotency tests

    • Command: npm test -- --run electron/ipc/handlers.test.ts electron/services/researchBrowserServer.test.ts
    • Files: electron/ipc/handlers.ts, researchBrowserServer.test.ts
    • Expected outcome: Repeated handler setup is safe.
    • Validation: Duplicate setup test.
  7. Fix server listener leak

    • Command: npm test -- --run server.test.ts
    • Files: server.ts, server.test.ts
    • Expected outcome: No MaxListenersExceededWarning.
    • Validation: Full test run without listener warnings.
  8. Run full validation after fixes

    • Command: npm run ci
    • Files: Whole repo
    • Expected outcome: Lint, typecheck, coverage, audit, build, contracts, dist all pass.
    • Validation: Exit code 0.
  9. Normalize repo/artifact naming

    • Command: rg -n "Windows-Venice-API-connector|Venice-API-connector|Venice_Forge" .
    • Files: README, docs, package metadata, zip scripts.
    • Expected outcome: Only canonical/current names remain outside historical docs.
    • Validation: Add verifier check.
  10. Create canonical report index

  • Command: npm run verify:markdown-links
  • Files: docs/DOCS_INDEX.md, docs/reports/CANONICAL_REPORT_INDEX.md (new)
  • Expected outcome: Historical reports are mapped, not blindly deleted.
  • Validation: Markdown links pass.

9. Validation Command Matrix

Area Command Expected result Notes
Install npm ci Pass Passed locally; deprecated transitive warnings
Lint npm run lint Pass Passed
ESLint only npm run lint:eslint Pass Covered by lint
Typecheck npm run typecheck Pass Passed
Unit tests npm test -- --run Pass Timed out locally; release hardening failure observed
Coverage npm run test:coverage Pass Timed out locally; same failure path
Build npm run build Pass Passed
Release hardening npm run verify:release-packaging-hardening Pass Failed after .config/*.local.yaml generation
Contracts npm run verify:contracts Pass Not fully run after known release hardening failure
Package Windows npm run dist:win Pass Not run in Linux sandbox
Package macOS npm run dist:mac Pass Not run in Linux sandbox
Package portable npm run dist:portable Pass CI workflow has Windows portable smoke path
Electron smoke npm run smoke:electron Pass Requires packaged app / platform env
Electron IPC npm test -- --run electron/ipc/handlers.test.ts electron/services/researchBrowserServer.test.ts Pass Add duplicate setup regression
Stream lifecycle npm test -- --run src/hooks/use-chat.test.ts Pass after behavior update Existing test asserts old abort behavior
Storage/privacy npm run verify:storage-privacy Pass Script exists; not run in this pass
Document ingestion npm run verify:document-ingestion Pass after semantic additions Current verifier misses body delimiter issue
Docs/repo hygiene npm run verify:markdown-links && npm run verify:repo-handoff-hygiene Pass Needed after docs cleanup

10. Remaining Unknowns

1. Git branch and HEAD could not be verified because the uploaded ZIP has no .git directory.
   Needed: run from actual checkout:
   git status --short
   git branch --show-current
   git rev-parse --short HEAD

2. Live Venice API behavior was not tested because no real API key was provided or needed for static audit.
   Needed: configured secure key and controlled diagnostics/API smoke test.

3. Windows packaged installer behavior was not tested in this Linux sandbox.
   Needed:
   npm run dist:win
   npm run verify:dist:win
   npm run verify:dist:portable

4. macOS DMG/ZIP signing/notarization behavior was not tested in this Linux sandbox.
   Needed:
   npm run dist:mac
   npm run verify:dist:mac

5. Full test and coverage completion could not be confirmed because both timed out locally after the release-hardening/config contaminant issue surfaced.
   Needed: fix P0 archive/config issue, then rerun:
   npm test -- --run
   npm run test:coverage

6. Visual UI defects such as light-theme invisible text and bottom-left menu squish need screenshot or Playwright-style visual regression confirmation.
   Needed: targeted visual tests or captured QA snapshots across themes and desktop sizes.

11. 2026-06-21 09:50 Snapshot Follow-up

This section tracks the follow-up repair pass against the ZIP snapshot audited at Windows-Venice-API-connector-clean-20260621-095008.zip.

Completed items

  • P0 — Testing: Fix verify:document-ingestion hang caused by forced serial jsdom component tests

    • Root cause: scripts/verify-document-ingestion.cjs ran all ingestion service tests and jsdom component tests in a single Vitest invocation with --fileParallelism=false. The component tests deadlocked when forced serial.
    • Fix: Split the verifier into two invocations: ingestion tests still run serial (--fileParallelism=false), component tests run in parallel.
    • Files: scripts/verify-document-ingestion.cjs, scripts/verify-document-ingestion.test.ts.
    • Validation: npm run verify:document-ingestion passes (99 tests, ~7 s); npm run verify:contracts passes.
  • P1 — Testing: Segment the full Vitest suite to prevent 600 s+ opaque hangs

    • Fix: Added test:server, test:electron, test:ingestion, test:ui, test:unit, and test:ci scripts to package.json. Updated .github/workflows/ci.yml and .github/workflows/release.yml to use the segmented scripts. Updated scripts/verify-release-packaging-hardening.cjs to accept npm run test:ci. Added package-scripts.test.ts regression guards.
    • Files: package.json, .github/workflows/ci.yml, .github/workflows/release.yml, scripts/verify-release-packaging-hardening.cjs, package-scripts.test.ts, AGENTS.md.
    • Validation: npm run test:ci passes (272 files / 3,393 tests / 1 skipped, coverage thresholds met); all individual segments pass; zero overlap confirmed.
  • P1 — Logging/Security: Redact dev/test logger output by default

    • Root cause: src/shared/logger.ts forwarded raw arguments to console.warn/console.error in dev/test, so paths and token-shaped strings appeared in stderr.
    • Fix: Hardened logger.warn/logger.error to recursively sanitize arguments through redactSecrets/sanitizeErrorText before printing. Added venice_ token pattern to src/shared/redaction.ts. Added src/shared/logger.test.ts regression guards.
    • Files: src/shared/logger.ts, src/shared/logger.test.ts, src/shared/redaction.ts, src/shared/redaction.test.ts, src/stores/chat-stream-manager.ts.
    • Validation: npm test -- --run src/shared/logger.test.ts src/hooks/use-chat.test.ts src/stores/chat-stream-manager.test.ts src/shared/redaction.test.ts passes (48 tests); stderr no longer contains /Users/, Bearer, venice_, or sk- shaped values.

Validation summary for this follow-up pass

Command Result
npm run lint:eslint Pass (0 warnings)
npm run typecheck Pass (renderer + electron main)
npm run build Pass
npm run verify:release-packaging-hardening Pass (102 checks)
npm run verify:document-ingestion Pass (99 tests)
npm run verify:contracts Pass (all 22+ sub-verifiers)
npm run test:ci Pass (272 files / 3,393 tests / 1 skipped, coverage thresholds met)