Skip to content

Fix 3 bugs found in QA: chat stale project, contradiction dedup race, synthesis gate link#4

Merged
adbarc92 merged 7 commits intomainfrom
feature/qa-bugfixes
Apr 21, 2026
Merged

Fix 3 bugs found in QA: chat stale project, contradiction dedup race, synthesis gate link#4
adbarc92 merged 7 commits intomainfrom
feature/qa-bugfixes

Conversation

@adbarc92
Copy link
Copy Markdown
Owner

@adbarc92 adbarc92 commented Apr 19, 2026

Summary

Seven commits fixing four bugs found in QA against docs/REQUIREMENTS.md, plus the QA assets (checklist + fixtures) that verify them.

Bug fixes

Bug Requirement Commit
Chat jammed after stored project disappears — sidebar disagreed with chat shell, queries 404'd FR-1.2 5a684bc (reload path) + d31ab05 (no-reload self-heal on 404)
Duplicate contradiction records under concurrent upload FR-4.1 02fc817
Synthesis gate message was static red text, not a link FR-5.1 3bc3775
Chat leaked messages across project switches (implicit) 46d5510

Supporting work

  • f205ac2QA checklist (docs/qa/pr-4-checklist.md): 10 scenarios tagged by commit
  • c800a8fQA fixtures (docs/qa/fixtures/): contradicting-pair + Aegis Station demo corpus so the checklist is reproducible anywhere

Test plan

  • Backend: uv run pytest tests/ -v -m "not integration" — 87/87 pass
  • Frontend: npm test — 175/175 pass (new: ProjectContext reconciliation, useChat 404 self-heal, useChat reset-on-switch)
  • Alembic migration e5f6a7b8c9d0 applied to dev DB; unique-pair index rejects duplicate inserts
  • Live-app QA via browser-bridge: 10/10 scenarios PASS with the seeded Aegis Station project
    • S6 concurrent upload: produced 1 contradiction (was 2 pre-fix)
    • S8 gate link: singular grammar, underlined, navigates to /contradictions
    • S9 end-to-end synthesis: 9 sections, 41KB, .md download HTTP 200
    • S10 stale-project reload: reconciled to "Select a project..."
  • Reviewer: eyeball the checklist, run it if you want (cd into repo, follow scenarios — all paths are now repo-relative)

Files changed

Backend

  • backend/alembic/versions/20260418_contradiction_unique_pair.py (new)
  • backend/app/models/contradiction_repository.py
  • backend/app/services/contradiction_service.py

Frontend

  • frontend/src/contexts/ProjectContext.tsx
  • frontend/src/contexts/ProjectContext.test.tsx (new)
  • frontend/src/lib/api.ts (adds ApiError class)
  • frontend/src/hooks/useChat.ts
  • frontend/src/hooks/useChat.test.tsx (moved from .ts, expanded)
  • frontend/src/pages/SynthesisPage.tsx
  • frontend/src/pages/SynthesisPage.test.tsx

Docs + QA

  • docs/qa/pr-4-checklist.md (new)
  • docs/qa/fixtures/README.md (new)
  • docs/qa/fixtures/contradicting-pair/doc-{a,b}.md (new)
  • docs/qa/fixtures/aegis-demo/*.{md,txt,pdf} (new)

ProjectContext hydrated activeProject from localStorage without checking
whether the project still existed server-side. If a project was deleted
in another session, the chat page would render its empty state and any
query would 404. The sidebar dropdown would also disagree with itself
(stored ID not in the fetched list, so it fell back to the placeholder).

Reconcile the stored project against useProjects() once the list loads
and clear it if the ID isn't present.
Uploading multiple contradictory documents back-to-back spawned parallel
background scans that each detected the same chunk pair (in opposite
orders) before either had committed, producing two contradiction records
for what the spec requires to be one.

Add a functional unique index on (project_id, LEAST(chunk_a_id, chunk_b_id),
GREATEST(chunk_a_id, chunk_b_id)) so the DB enforces pair uniqueness
regardless of insertion order. Have the repository catch IntegrityError
from the race-loser insert and return None; the service then skips the
found++ bump instead of propagating the error.
The synthesis gate error told users to resolve open contradictions but
the text was static, forcing them to find the nav link themselves.
Requirement FR-5.1 specifies the message should link to the
contradictions page.

Wrap the word 'contradiction(s)' in a Link and fix singular/plural
grammar for the count=1 case.
The earlier fix cleared a stale activeProject only when useProjects
refetched. If the project disappeared while the user stayed on one page
(no navigation, no window focus), the cache stayed warm, reconciliation
never fired, and the next chat send 404'd with 'Error: Project not
found' — leaving the user stuck.

Introduce ApiError (carries the HTTP status) and have useChat catch
status=404 by clearing activeProject and invalidating the projects
query. The chat page then falls back to its 'Select a project'
placeholder on the next render.
useChat held messages in local state that persisted across project
switches, so chatting in project A, switching to B, and scrolling up
would show A's messages alongside B's — and the next send would carry
stale visual context into the new project.

Apply the same render-phase reset pattern SynthesisPage uses: track the
previous projectId and clear messages + isLoading when it changes.
Documents and Contradictions already isolate state via React Query
keyed on projectId; this brings Chat in line with that behavior.
Ten-scenario manual verification covering all four bug-fix commits plus
general chat, documents, contradictions, and synthesis flows. Tied to
the seeded Aegis Station demo project.
The checklist referenced tester-local paths under /tmp and a Windows
Temp dir — fine for the original run but unusable for anyone else.

Promote both fixture sets into docs/qa/fixtures/: the small
contradicting pair used by scenario 6, and the six-document Aegis
Station demo referenced by the reseed block. Update checklist paths
accordingly and add a README explaining what each set is for.
@adbarc92 adbarc92 merged commit 514d7da into main Apr 21, 2026
5 of 7 checks passed
@adbarc92 adbarc92 deleted the feature/qa-bugfixes branch April 21, 2026 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant