feat(batch-analysis): document grids with triage, verification, cited exports, and grid Q&A - #7299
Draft
joeyorlando wants to merge 23 commits into
Draft
feat(batch-analysis): document grids with triage, verification, cited exports, and grid Q&A#7299joeyorlando wants to merge 23 commits into
joeyorlando wants to merge 23 commits into
Conversation
joeyorlando
force-pushed
the
joeyorlando/batch-analysis
branch
from
August 17, 2026 14:09
9225716 to
4363a58
Compare
joeyorlando
force-pushed
the
joeyorlando/batch-analysis
branch
from
August 17, 2026 21:38
4363a58 to
5641c7d
Compare
joeyorlando
force-pushed
the
joeyorlando/batch-analysis
branch
from
August 17, 2026 22:05
5641c7d to
4139b05
Compare
joeyorlando
force-pushed
the
joeyorlando/knowledge-files
branch
from
August 17, 2026 22:58
4d45d33 to
9bff928
Compare
joeyorlando
force-pushed
the
joeyorlando/batch-analysis
branch
3 times, most recently
from
August 17, 2026 23:21
ab3cb9c to
9a657ae
Compare
joeyorlando
force-pushed
the
joeyorlando/batch-analysis
branch
from
August 18, 2026 00:15
9a657ae to
becbb65
Compare
pull Bot
pushed a commit
to bryanwills/archestra-ai
that referenced
this pull request
Aug 18, 2026
…ity, indexing, and OCR for scans (archestra-ai#7321) Closes archestra-ai#7201. A repository for documents you upload to Archestra directly — a connector pulls from a source system; here you bring the file yourself. Split out of the combined branch so it can land on its own; batch analysis (archestra-ai#7299) stacks on top of this branch. ## What this does - **Knowledge > Files**: upload PDF / Word / Markdown / CSV / JSON / HTML / text (drag-and-drop batches), organize into flat directories, rename, delete, search. Bytes live in Postgres (`kb_files.data`), capped by `ARCHESTRA_KNOWLEDGE_FILES_MAX_UPLOAD_BYTES` (25 MB default). - **Per-item visibility** — Organization / Teams / Only me — on both files and directories, enforced in the repository *and* carried into retrieval as direct ACL tokens, so sharing a knowledge base never widens who can read a document. - **Indexing**: select files or whole directories → **Add to knowledge base** (existing or created from the selection). Each base gets one internal `file_upload` connector (never user-selectable, hidden from connector management); documents upsert on `(connector_id, source_id)` so re-indexing refreshes rather than duplicates. Chunking goes through the same `chunkAndStoreDocument` path connector syncs use. - **Save from chat**: promote a conversation attachment to the repository — from the attachment chip on a message or multi-selected in the Files panel — choosing name, directory, and visibility, with optional immediate indexing. Attachment/file preview (including PDFs, via a scoped CSP change) works from both chat and the repository. - **Scanned PDFs + Document OCR** (lands on top of archestra-ai#7318): a scanned PDF is *accepted* at upload when the organization has Document OCR configured and transcribed at indexing time through the same page-bounded pipeline connector syncs use (shared per-request page budget, per-document cap, metered `knowledge:ocr` interactions). Without OCR configured, the honest 400 at upload stays. A failed transcription fails that file's indexing with a reason naming the OCR outcome — never a silently empty document. - **Dead path removed**: `extractAndIngestDocuments` — the long-disabled chat auto-ingestion stub archestra-ai#7201 describes — is deleted along with its fire-and-forget call site and test mocks. Explicit promotion with user-chosen visibility is the deliberate answer to "what happens to chat uploads": nothing is silently copied into a knowledge base, and what the user promotes carries an ACL no wider than they chose. ## Testing - Backend: upload/visibility/directory/indexing route tests, promote-from-chat tests, extraction tests, and OCR-conjunction tests where the only fake is the Anthropic wire (MSW): scanned upload accepted → indexed → transcription retrievable; failed transcription → named per-file failure. Knowledge-base suite: 1446 tests green. - Frontend: repository page, dialogs, save-from-chat components, preview-kind tests. 1164 tests green across knowledge/chat suites. - Verified live in Chrome (see the walkthrough notes on the PR thread). Docs: Knowledge Files is a subsection on the Knowledge page (no separate page). Env vars documented in platform-deployment.md + `.env.example`. --- <!-- archestra-banner:v1 --> <a href="https://archestra.ai/contributor-onboard" rel="nofollow noreferrer noopener" target="_blank"> <img alt="Archestra Contributor" src="https://raw.githubusercontent.com/archestra-ai/archestra/main/docs/assets/archestra-contributor-banner.webp"/> </a>
…nner, and UI Restores the batch-analysis half of the former combined branch on top of the knowledge-files base: grid analyses with per-cell citations, row sources (knowledge documents, repository files, inline text), the analysis task-queue lane, RBAC resource, and the /batch-analyses UI. Scanned repository files now resolve through Document OCR when the organization has it configured, so a corpus of scans can be analysed.
Swaps the hand-rolled <table> on the analysis detail page for react-data-grid (MIT, zero dependencies, React 19 peer): rows are virtualized so hundreds of sources stay smooth, the Source column stays frozen under horizontal scroll, columns are resizable, and the grid is arrow-key navigable like a spreadsheet — Enter or click opens the answer's detail sheet. Cell renderers (status, citations, file preview, hover delete) carry over unchanged; the grid's theme variables map onto the shadcn tokens so it follows the app palette and dark mode.
… the route The stylesheet moves from the component to the route file because the tailwind postcss config cannot process CSS imports under vitest. The new tests pin what the swap must preserve: statuses render per cell, an answer cell opens the detail sheet while the source column does not, and an uploaded source's label opens the file preview.
… closing it, and hand focus back to the grid Two keyboard-flow fixes found testing in the browser: the browser's default Enter activation clicked the sheet's just-focused close button (open, then closed 9ms later) — preventDefault stops it; and a controlled sheet has no trigger for Radix to return focus to, so closing it dropped focus on body and killed the grid's arrow-key navigation — onCloseAutoFocus now hands focus back to the active cell.
…urface Running an analysis with a GitHub Copilot codex model failed with 'model … is not accessible via the /chat/completions endpoint': those models are Responses-API-only, and which surface a model needs lives on the synced model row — the executor called createLLMModel without that lookup, so every Copilot model fell back to the chat-completions default. Same resolution createLLMModelForAgent does.
…add in-grid grow affordances
Three gaps found using the sheet live: a pasted-text row was a dead
label ('is Acme MSA a document?') — every source now carries a type icon
and pasted text opens in a preview dialog, like uploaded files already
did; removing a row was a single un-confirmed click on a hover-revealed
trash can — it now goes through the standard delete confirmation naming
the row; and growing the sheet meant reaching for the toolbar — a pinned
bottom row offers a quiet 'Add row' and a slim trailing '+' header opens
the column editor, spreadsheet-style.
…psis The grid's cells inherit nowrap + text-overflow: ellipsis; the 28px button plus the cell's inline padding and border overflowed the 44px stub column by a pixel, which painted a literal '…' beside the plus. Clip instead of ellipsize and give the stub enough width.
Answer cells copy their answer text; the source column copies the pasted text (or the row label for document-backed rows).
…a model call failed
Two findings from a 50-row × 12-column run against claude-sonnet-5:
The executor pinned temperature: 0 on every call, and Anthropic's
thinking-by-default generations reject any explicit temperature
('temperature may only be set to 1 when thinking is enabled or in
adaptive mode') — every row 400ed. The knob is now omitted for models
that reject it (Anthropic thinking generations, OpenAI reasoning
generations); elsewhere 0 stays for run-to-run comparability.
Diagnosing that took longer than it should have: the cell error was a
bare 'Model call failed: Bad Request' because the AI SDK falls back to
the HTTP statusText when the error body is not in the provider's wire
shape. The executor now surfaces the response body's own message
alongside it.
Verification: a completed cell can be marked verified (transactional, done-cells-only, scoped through the analysis), stamped with the reviewer and shown with their name; regeneration of any kind clears the mark, and the sign-off survives reviewer deletion (verified_at is the predicate, mirroring the catalog approval columns). Audited through a dedicated registry entry whose snapshot digests the verified set, so even count-neutral changes produce a real diff. Flags: a column can opt in to triage — the same per-row model call then classifies each answer green/yellow/red/grey. Nullish in the row contract so a model omitting the flag never fails the row; volunteered flags on non-opted-in columns are discarded. Dots in the grid, badges in the cell sheet.
…emplate catalog Review follow-ups on verification/flags: the verification UPDATE re-asserts status='done' so a concurrent regeneration can't be signed off (zero rows rolls the batch back); retry refuses before resetting when a run is active, so a refused dispatch no longer wipes a cell's flag and sign-off; a column opting out of triage clears its stored flags and the UI renders flags only for opted-in columns; the audit digest includes verifiedAt so re-verification diffs. Templates: eight predefined column sets (NDA, services, lease, employment, share purchase, credit, settlement, data protection) served by GET /api/batch-analysis-templates — static constants validated against the column schema in tests; applying one only copies columns.
…d sorting Templates: the wizard's columns step offers the predefined sets (picking one replaces the draft, editable afterwards); the edit dialog appends a template's columns with collision-safe keys so existing answers stay attached — templates never replace configured columns there. Filter/sort: the grid's answer columns are sortable with format-aware comparators (numbers through currency noise, dates, yes/no), unknowns pinned last in either direction and the row's own order as tie-breaker so polling refetches don't reshuffle equals. A toolbar filters by substring across labels and answers, by triage flag, and to fully verified rows, showing x-of-N when active.
Export ships the current view (filtered and sorted) as CSV or Excel with provenance intact: every answer column is paired with its supporting text, flagged columns carry the triage rating, and verified answers name the reviewer. CSV is RFC 4180 with leading = + - @ neutralized so model output can never execute as a spreadsheet formula; Excel adds a Citations sheet and loads ExcelJS on demand, never with the page. Citations in the cell sheet are now click-through: for text-ish sources the preview marks and scrolls to the quote (same whitespace-normalized matching the grounding check uses); pasted-text rows highlight in their own dialog; PDF/markdown/html previews show the quote above the viewer with a find-in-page hint, since those renderers are browser-owned and citations carry no page metadata.
Ask a question of the table itself — not the source documents — and get an answer citing the cells it relied on; chips in the panel jump the grid to the cited cell and open its detail sheet. References are validated against exactly the cells serialized into the prompt, so a hallucinated or foreign coordinate never reaches the client, and the serialization stops at a size budget while saying how many rows it dropped. Deliberately unpersisted: the durable artifact is the grid. The agent-model resolution the row executor uses (key guard, multi-surface endpoint lookup, reasoning-model temperature omission) moves into a shared resolveBatchAnalysisModel so the chat path cannot drift from it. Gated by batchAnalysis:read plus chat:read, the standard key-spending gate.
Retry's cell reset now rides inside the run start, after its single-flight check — the check/reset/start sequence had a window where a concurrent run could win and the refused dispatch had already wiped the cell's answer state. Grid-chat marks cells citable only when their row actually fit the serialization budget, so the model cannot get an omitted cell 'validated'. Cell verification applies as two bulk UPDATEs (verify set, unverify set) instead of one round trip per entry, still all-or-nothing behind the done-only predicate. CSV formula neutralization tolerates leading whitespace and control characters, the same way spreadsheets do when deciding to execute a cell. The flag filter matches only columns that currently opt into triage, mirroring how stale flags are hidden everywhere else.
- Relay grid-chat provider failures as upstream-marked 502s using the shared describeModelCallError (moved to batch-analysis/llm.ts) - Tolerate duplicate entries in cell verification (dedupe keep-last) - Un-export internals (buildGridChatSystemPrompt), move CellVerificationError above the model class per module-order rules - Wrap highlight-split text siblings in spans (machine-translation rule) - Restore deleteCellsForRemovedColumns docblock placement
…xport in Batch Analysis
… horizontal card strip
…after rebase onto main
joeyorlando
force-pushed
the
joeyorlando/batch-analysis
branch
from
August 18, 2026 17:27
379c4ef to
88fff66
Compare
…audit) — transitive dep of exceljs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intended for a dev-image build rather than immediate merge (label
deploy-dev-platform-images; image tag = head SHA).Ask the same questions across many documents and get a grid of answers back. An analysis is a set of columns (questions) run over a set of rows (sources); every cell is one model answer with a verbatim supporting quote from the source — plus the review workflow that makes the grid trustworthy at scale.
What this does
The grid
knowledge:ocrinteractions.analysislane (ARCHESTRA_BATCH_ANALYSIS_WORKER_MAX_CONCURRENT); a row fetches and pays for its source once regardless of column count. Source text is capped atARCHESTRA_BATCH_ANALYSIS_MAX_SOURCE_CHARSwith truncation recorded on the cell, never silent.batchAnalysisRBAC resource (read/create/update/delete/execute) and org/team/personal visibility enforced on every path — read, edit, delete, add rows, run, retry — so an analysis someone cannot see is also one they cannot spend credentials on. Audited create/update with before/after diffs.The review workflow
Testing
Backend: visibility matrix across all action paths, source-resolution ACL tests (kb_document, kb_file incl. scanned-with-OCR via an MSW-mocked wire, inline text), runner/executor/prompt tests, verification/templates/grid-chat route tests (96 in the batch-analysis suites). Frontend: wizard, grid, dialogs, column-key stability, answer-view filter/sort, export neutralization, quote-highlight tests. Verified end to end in Chrome against a 50-document × 12-column run: sort, flag/verified filters, citation → highlighted quote, verification badges, Ask panel with clickable references, templates, exports.
Rebased onto main; the branch's migrations were regenerated as
0422_batch_analysis/0423_eager_donald_blakeafter main's0421_mcp_idle_hibernation.