PR #233's CI failed with "npm ci can only install with an existing
package-lock.json" even though the file existed: a "Merge branch 'main'"
commit had auto-merged backend/package.json and package-lock.json into
invalid JSON with no conflict raised, and npm reports an unparseable
lockfile as if it were missing.
Two guards: .gitattributes marks package-lock.json/bun.lock merge=binary so
concurrent lockfile changes surface as explicit conflicts (resolve by
regenerating, never hand-merging), and CI parse-checks package.json and the
lockfile before npm ci so any corruption that still lands fails with the
real reason.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The "Merge branch 'main'" commit (71a7aba) auto-merged backend/package.json
and backend/package-lock.json without raising a conflict, but git's
line-level merge produced invalid JSON in both files: package.json lost the
comma after the new "test:stack" script, and package-lock.json lost the two
closing-brace lines of the supertest/cookie-signature entry. npm treats an
unparseable lockfile as absent, which is why CI failed with the misleading
"npm ci can only install with an existing package-lock.json".
package.json: restore the comma. package-lock.json: rebuilt from main's
known-good copy via `npm install --package-lock-only` against this branch's
package.json. Verified locally: npm ci, npm test (259 passed), npm run build.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every directory picker (AddDocumentsModal, UseWorkflowModal, the assistant
project selector) loads its "Projects" tab through useDirectoryData, which
fired one GET /projects/:id for EVERY existing project the moment the modal
opened. Each of those requests costs an auth verification against GoTrue
plus ~6 PostgREST queries, so an account with N projects produced an
~8xN-request burst on the Supabase gateway per modal open.
Under that burst the gateway genuinely falls over: measured locally against
the Supabase CLI stack, overlapping modal-open storms drove GoTrue into
Postgres connection exhaustion ("failed to connect ... context deadline
exceeded") and produced 467x 500 + 641x 504 on /auth/v1/user in a single
run — surfacing to users as failed project creates/loads, and to the e2e
suite as the intermittent Kong 502s its specs currently retry around.
Fix: GET /projects now accepts ?include=documents and returns each
project's documents from one batched query (same attach helpers as
GET /projects/:id, run once across all documents), and useDirectoryData
uses it. A modal open is now 1 API request and a fixed number of DB
queries regardless of project count. After the change the same storm
harness produced zero 5xx and zero auth failures.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Six new unit suites (102 tests) for the highest-risk untested libs:
userDataCleanup (destructive account/project deletes), documentVersions
(document integrity), chat/citations (legal-citation parsing), safeError
(secret redaction), llm/models (model resolution), and userLookup
(profile email sync). Coverage floors ratchet up 2/2/4/2 ->
11/10/14/10 (measured 11.18/10.98/14.43/10.91), and
docs/testing-coverage.md gives contributors a prioritized backlog for
the remaining untested libs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Takes frontend/npm run lint from 23 errors / 40 warnings to 0 errors /
40 warnings, then removes continue-on-error from the CI lint step so it
gates merges.
Errors fixed outright:
- @typescript-eslint/no-explicit-any (5, ChatView.tsx): dropped
redundant (msg as any) casts — Message already declares files,
workflow, and error with the exact shapes the props expect.
- react/no-unescaped-entities (1, support/page.tsx): "We'll" ->
"We'll".
Targeted disables (17, all react-hooks/set-state-in-effect): every site
is an intentional effect-driven state pattern — SSR/hydration mount
gates (Modal, useSelectedModel), reset-on-prop/identity-change
(CaseLawPanel x3, ChatView chat switch, CitationQuotesHeader,
ChatHistoryContext logout, useFetchDocxBytes), sync fast paths of async
fetch/check effects (CaseLawPanel, MfaLoginGate x2), DOM-measured state
(ChatView scroll button, message-visibility restore), and timed UI
latches (PreResponseWrapper, TRChatPanel, AskInputPopup auto-submit).
Rewriting any of them would change runtime behavior, so each carries a
// eslint-disable-next-line with a one-line reason instead of a fix or
a repo-wide rule change.
CI: lint step is now blocking; comments updated to say the backlog is
at zero.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ports the backend route-level integration suites from the amal66 fork
(index: Open-Legal-Products/mike#205), adapted from the fork's
apps/api modules/services layout to this repo's monolithic
backend/src/routes/*.ts layout.
app/index split (the only production change; mechanical, zero behavior
change): backend/src/index.ts previously built the express app and
called app.listen at the bottom. Everything except the listen call
moved verbatim into backend/src/app.ts, which now exports `app` (same
middleware order, same routes, same rate-limiter setup, dotenv/config
still imported first). index.ts is now a tiny entry that imports
{ app } and calls listen with the same log line. `npm run build` still
emits dist/index.js and the `start` script is unchanged.
New suites under backend/src/__tests__/integration/ (94 tests):
- health.test.ts (5): /health, requireAuth 401 paths, 404 fallthrough
- chat.routes.test.ts (6): POST /chat validation + SSE happy/error paths
- projects.routes.test.ts (18): overview/create/detail/patch/delete,
sharing normalisation, ownership guards
- projectChat.routes.test.ts (3): project access guard + SSE paths
- tabular.routes.test.ts (31): review CRUD, access guards,
document-access filtering, missing_api_key guards
- user.routes.test.ts (27): profile, API-key crypto boundary, MFA
guards, export/deletion endpoints
- documentsUpload.routes.test.ts (4): upload validation + download-zip
bounds/access
- access.supabase.test.ts (1) + stack.supabase.test.ts (4): gated on
SUPABASE_TEST_URL / SUPABASE_TEST_SERVICE_ROLE_KEY (stack suite also
needs SUPABASE_TEST_ANON_KEY); describe.skip otherwise
- scripts/test-stack.sh + `npm run test:stack`: reads a running
`supabase status -o json` and runs the gated suites
Adds supertest + @types/supertest as devDependencies.
Dropped relative to the fork (subjects that do not exist in this repo):
- orgs.routes, credits.concurrency.supabase, dmsConnectors suites
(fork-only features)
- all credit-reservation cases (429 CREDIT_LIMIT_EXCEEDED,
reserve-then-refund) in chat/projectChat: no credit system here
- health /ready case: no /ready endpoint here
- org-membership project access case: no org model here
- upload magic-byte validation cases: this repo validates extension
only (the fork adds content sniffing; replaced with a missing-file
400 case)
- download-zip 50-document cap case: no cap here (replaced with a
no-accessible-documents 404 case)
- stack.supabase PUBLIC_TABLES updated to this repo's schema; the
fork's credit-RPC coverage is n/a
Verified locally: backend `npm test` -> 8 files passed, 2 skipped;
106 tests passed, 5 skipped (gated suites skip without env).
`npm run build` passes. The gated suites were additionally run against
a live local Supabase stack: 2 files, 5/5 tests passed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ported from the amal66 fork (index: Open-Legal-Products/mike#205), adapted
to this repo's frontend/ layout and current component behavior. Adds the
jsdom + testing-library harness on top of the existing vitest setup:
vitest.config.mts (jsdom environment, @/ alias mirroring tsconfig paths,
dummy Supabase env for modules that build a client at import time) and
vitest.setup.ts (jest-dom matchers). New devDependencies: jsdom,
@testing-library/react, @testing-library/jest-dom,
@testing-library/user-event, @vitejs/plugin-react.
Ported suites (30 new tests):
- FileTypeIcon.test.tsx (11) — fileTypeKind mapping + icon rendering
- TRTable.test.tsx (1) — header/row render smoke test; the fork's ARIA
role assertions (table/columnheader, "Tabular review" label) target
fork-only markup — this repo's grid is div-based, so the test asserts
rendered content instead
- button.test.tsx (4), pill-button.test.tsx (8), cite-button.test.tsx (3)
- useSmoothedReveal.test.ts (3) — passes against this repo's early-return
snap behavior unchanged
Dropped (subjects don't exist here):
- HistoryDropdown.test.tsx — no tr-chat-panel/HistoryDropdown component
- applyAssistantStreamEvent.test.ts — no such module
- useProjectsQuery/useTabularReviewsQuery/useWorkflowsQuery tests —
react-query is fork-only
- lib/toast.test.ts — no frontend/src/lib/toast.ts
- useAssistantChat.parsers.test.ts — the parser helpers exist inside
useAssistantChat.ts but are not exported, and the fork's extracted
module doesn't exist here
Verified: frontend npm test 38/38 passing (30 new + 8 pre-existing cn()
utils tests); npx tsc --noEmit clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ported from the amal66 fork (see index Open-Legal-Products/mike#205)
onto current main, adapted to this repo's backend/ layout
(apps/api/src/lib -> backend/src/lib), plus a v8 coverage ratchet.
Suites ported (51 new tests, verified locally):
- access.test.ts (7): owner/shared/private project access, doc access,
review sharing, document-ID filtering. Dropped the fork's "org RBAC"
describe block (7 cases) — org_id/org_members multi-tenancy and the
role/canManage fields do not exist in this repo's access.ts.
- storage.test.ts (25): filename normalization/sanitization, RFC 5987
encoding, Content-Disposition, storage key helpers. Dropped the fork's
vi.mock of lib/env — this repo has no env module; storage reads
process.env directly and the tested helpers are pure.
- userApiKeys.test.ts (10): normalizeApiKeyProvider + hasEnvApiKey.
Added a beforeEach env clear so shell-exported API keys can't leak
into assertions.
- chatTypes.test.ts (9): resolveDoc/resolveDocLabel, which live in
lib/chat/types.ts here (the fork's lib/chatTools.ts equivalent).
Dropped generateSpotlightNonce cases (2) — no such export here.
Suites dropped entirely (subject not present in this repo):
- upload.test.ts — tested hasMagicBytes; this repo's lib/upload.ts is
only the multer middleware and exports no magic-byte checker.
- userSettings.test.ts — tested resolveTabularModel (fork-only keyed-
provider fallback); this repo resolves tabular_model via
resolveModel with a static default.
Coverage ratchet: vitest.config.mts adds v8 coverage over src/lib/**
with floors measured against this tree (2.58% stmts, 2.00% branches,
4.61% funcs, 2.58% lines -> floors 2/2/4/2). Full suite: 5 files,
63 tests passing (incl. the pre-existing 12 in downloadTokens.test.ts);
npm run test:coverage and npm run build both pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a Testing section documenting every suite the testing PR series
introduces (unit/integration via vitest, Playwright e2e, offline evals,
gated real-Supabase stack tests), the expectation that changes carry tests
at the lowest layer that catches the regression, and a PR template (ported
from the amal66 fork, Open-Legal-Products/mike#205) whose checklist asks how
the change was verified. Intended as the capstone of the series — the
commands it documents are introduced by the sibling test PRs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ported from amal66/mike#46 onto current main, extended for this tree:
backend job runs npm ci, tests (--if-present, so it is safe to merge in any
order relative to the test-harness PR) and tsc build; frontend job runs
tests, eslint as an advisory step (main currently carries 23 lint errors —
flip to blocking once burned down), and a production next build with
placeholder NEXT_PUBLIC_* env (verified locally that the build succeeds and
contacts nothing); evals job runs node evals/run.mjs when present, else
skips.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ported from amal66/mike#24 onto current main; lockfiles regenerated against
this tree. Adds vitest as a dev dependency with a `test` script in both
packages, excludes test files from the backend tsc build, and seeds one
suite per package (backend: downloadTokens, 12 tests; frontend: cn() utils,
8 tests). Verified locally: backend 12/12, frontend 8/8 passing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
User messages are persisted with a `workflow` field
(backend/src/routes/chat.ts, projectChat.ts) and it is read back when
rendering chat history (ChatView -> UserMessage) to show which workflow a
message was sent under. But the column is never created by schema.sql or any
migration, so every user-message insert fails with PostgREST PGRST204
("Could not find the 'workflow' column of 'chat_messages'") and is dropped
silently (the insert result is not checked). The assistant insert has no
workflow column, so it succeeds — the net effect on a self-hosted install is
that reloading a thread shows the assistant reply but not the user's prompt.
Add the `workflow jsonb` column to schema.sql (fresh installs) and a
migration (existing installs), mirroring the adjacent content/files jsonb
columns.
- Refactor user profile loading and updating logic to improve state management and reduce unnecessary checks.
- Update MFA login gate to streamline verification checks and improve user experience.
- Ensure consistent handling of user ID across profile context and components.
- Improve error handling and loading states in user-related API calls.
- Add SecurityPage component for managing MFA settings, including enrollment and verification.
- Create MfaLoginGate to handle MFA verification state during login.
- Develop MfaVerificationPopup for user input of verification codes.
- Implement VerifyMfaPage for the MFA verification process after login.
- Introduce reusable VerificationCodeInput component for entering verification codes.
- Integrate Supabase MFA API for managing factors and verification.
- Add loading states and error handling for a better user experience.
- Refine CourtListener citation verification, bulk lookup logging, and API fallback behavior
- Persist cancelled chat stream output and render cancellation as the final assistant message
- Add document/version deletion safety fixes and shared warning/modal UI updates
- Sync document panel, case law panel, and response UI styling refinements
- Harden OSS sync script to preserve local env, dependency, and generated files