chore(M001/S01): auto-commit after complete-slice

This commit is contained in:
2026-03-24 12:27:04 +01:00
parent 9f03d123d0
commit 13d4e29336
22 changed files with 970 additions and 118 deletions
@@ -7,6 +7,11 @@ files:
- JobTrackerApi/Controllers/CorrespondenceController.cs
- JobTrackerApi/Program.cs
- JobTrackerApi.Tests/GmailControllerTests.cs
observability_surfaces:
- POST /api/gmail/import
- POST /api/gmail/import-thread
- persisted Correspondence.ExternalThreadId / ExternalFrom / ExternalTo fields
- JobTrackerApi.Tests/GmailControllerTests.cs repeat-import coverage
verification:
- $HOME/.dotnet/dotnet build JobTrackerApi/JobTrackerApi.csproj
- docker run --rm -v "$PWD":/src -w /src mcr.microsoft.com/dotnet/sdk:9.0 bash -lc '...dotnet test /tmp/gmailtests/GmailTests.csproj...'
@@ -14,7 +19,8 @@ verification:
Extended correspondence persistence and Gmail import continuity for S01.
What changed:
## What changed
- `Models/Correspondence.cs`
- added `ExternalThreadId`
- added `ExternalFrom`
@@ -33,9 +39,25 @@ What changed:
- added repeat thread import coverage
- retained ranking and owned-job scope coverage from T01
Verification:
## Verification
- Native API build passed with `$HOME/.dotnet/dotnet build JobTrackerApi/JobTrackerApi.csproj`
- Isolated Gmail controller tests passed in Docker (`5 passed`)
Important caveat:
- The repositorys main `JobTrackerApi.Tests` project still has unrelated pre-existing compile failures outside Gmail tests, so the exact planned command `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter GmailControllerTests` remains blocked by broader test drift. Gmail coverage itself passes when isolated.
## Verification Evidence
| # | Command | Exit Code | Verdict | Duration |
|---|---------|-----------|---------|----------|
| 1 | `$HOME/.dotnet/dotnet build JobTrackerApi/JobTrackerApi.csproj` | 0 | ✅ pass | ~1.2s |
| 2 | `docker run --rm -v "$PWD":/src -w /src mcr.microsoft.com/dotnet/sdk:9.0 bash -lc '...dotnet test /tmp/gmailtests/GmailTests.csproj...'` | 0 | ✅ pass | not recorded |
## Diagnostics
- Inspect persisted `Correspondence` rows for `ExternalMessageId`, `ExternalThreadId`, `ExternalFrom`, and `ExternalTo` to confirm Gmail imports keep thread identity plus sender/recipient labels.
- Hit `POST /api/gmail/import` twice with the same Gmail message id to confirm the duplicate-safe `Imported`/`Skipped` contract.
- Hit `POST /api/gmail/import-thread` twice with the same thread payload to confirm repeat imports skip already-linked message ids instead of duplicating correspondence.
- Read `JobTrackerApi.Tests/GmailControllerTests.cs` for the durable expected behavior around repeat single-message and repeat thread imports.
## Important caveat
The repositorys main `JobTrackerApi.Tests` project still had unrelated pre-existing compile failures outside Gmail tests when this task finished, so the exact planned command `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter GmailControllerTests` remained blocked at that point by broader test drift. Gmail coverage itself passed when isolated.
@@ -0,0 +1,54 @@
---
estimated_steps: 4
estimated_files: 4
skills_used:
- react-best-practices
- test
---
# T03: Wire ranked Gmail suggestions into the job workspace UI
**Slice:** S01 — Smarter Gmail import and matching
**Milestone:** M001
## Description
Deliver the user-facing part of the slice in the actual job workspace. The Correspondence tab should stop acting like a generic Gmail search box and instead open with job-aware ranked suggestions from the backend, explain why each candidate is relevant, let the user override with manual search when needed, and refresh the job-linked correspondence view after import.
## Steps
1. Update `job-tracker-ui/src/types.ts` to reflect the new backend candidate contract and enriched correspondence metadata from T01 and T02.
2. Pass job context from `job-tracker-ui/src/components/JobDetailsDialog.tsx` into `job-tracker-ui/src/components/Correspondence.tsx` so the Gmail tab can request job-aware suggestions without duplicating the job fetch.
3. Refactor `job-tracker-ui/src/components/Correspondence.tsx` to consume the backend-ranked suggestions, show thread/message match reasons and import state, keep manual Gmail query override/search available, and refresh the rendered correspondence list after successful imports.
4. Add `job-tracker-ui/src/correspondence-gmail-import.test.tsx` covering ranked suggestion rendering, reason/confidence display, thread vs single-message import actions, and refresh after import.
## Must-Haves
- [ ] The Gmail tab opens on job-aware ranked suggestions instead of using `scoreMessage(...)` as the primary intelligence.
- [ ] The UI still supports manual Gmail searching as a fallback override, but it no longer depends on freeform query heuristics for the core experience.
- [ ] The React test proves the user can see ranked suggestions and that importing updates the same jobs correspondence surface.
## Verification
- `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/correspondence-gmail-import.test.tsx`
- Manually inspect the dialog to confirm ranked reasons/import state are visible and the correspondence list refreshes after import.
## Observability Impact
- Signals added/changed: visible match reasons/confidence/import state in the Gmail tab and clearer import success/duplicate feedback toasts.
- How a future agent inspects this: read `job-tracker-ui/src/correspondence-gmail-import.test.tsx` and open the Correspondence dialog in the running app.
- Failure state exposed: the UI should distinguish no matches, already-imported candidates, loading states, and import failures instead of collapsing them into a generic empty list.
## Inputs
- `job-tracker-ui/src/components/JobDetailsDialog.tsx` — host dialog that already owns the job record.
- `job-tracker-ui/src/components/Correspondence.tsx` — current Gmail import UI with client-side ranking.
- `job-tracker-ui/src/types.ts` — frontend API contracts.
- `JobTrackerApi/Controllers/GmailController.cs` — T01/T02 backend Gmail candidate and import contract.
## Expected Output
- `job-tracker-ui/src/components/JobDetailsDialog.tsx` — passes job context into the correspondence tab.
- `job-tracker-ui/src/components/Correspondence.tsx` — renders job-aware Gmail suggestions and refreshed import behavior.
- `job-tracker-ui/src/types.ts` — matches the updated backend contract.
- `job-tracker-ui/src/correspondence-gmail-import.test.tsx` — proves the UI flow end to end at component level.
@@ -6,34 +6,59 @@ files:
- job-tracker-ui/src/components/Correspondence.tsx
- job-tracker-ui/src/types.ts
- job-tracker-ui/src/correspondence-gmail-import.test.tsx
observability_surfaces:
- job-tracker-ui/src/components/Correspondence.tsx Gmail tab linked-thread state
- /gmail/job-candidates requests with queryOverride
- /gmail/refresh-linked-threads workspace refresh requests
- job-tracker-ui/src/correspondence-gmail-import.test.tsx
verification:
- npm ci (job-tracker-ui)
- CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/correspondence-gmail-import.test.tsx
---
Wired the job-aware Gmail matching contract into the actual job workspace UI.
Wired the job-aware Gmail matching contract into the actual job workspace UI and completed the live linked-thread refresh loop.
## What changed
What changed:
- `job-tracker-ui/src/types.ts`
- added frontend contracts for job-aware Gmail matches, match reasons, import results, and enriched correspondence metadata
- added frontend contracts for job-aware Gmail matches, linked-thread refresh results, import results, and enriched correspondence metadata
- `job-tracker-ui/src/components/JobDetailsDialog.tsx`
- now passes the loaded `job` into `Correspondence` so the Gmail tab can stay job-aware without another job fetch
- passes the loaded `job` into `Correspondence` so the Gmail tab can stay job-aware without another job fetch
- `job-tracker-ui/src/components/Correspondence.tsx`
- replaced client-side Gmail ranking as the primary workflow
- Gmail tab now calls `/gmail/job-candidates`
- shows confidence, score, match reasons, and already-linked state
- preserves manual query override via the same job-aware endpoint
- refreshes correspondence plus Gmail candidate state after single-message and thread imports
- automatically calls `/gmail/refresh-linked-threads` when the job already has linked Gmail thread ids
- refreshes correspondence plus Gmail candidate state after single-message, thread, and linked-thread refresh actions
- renders persisted Gmail metadata (`ExternalThreadId`, `ExternalFrom`, `ExternalTo`) in the correspondence view
- shows linked-thread freshness/import summary inside the Gmail area
- `job-tracker-ui/src/correspondence-gmail-import.test.tsx`
- verifies ranked Gmail suggestions render with visible reasons/confidence
- verifies single-message import refreshes the same jobs correspondence view
- verifies automatic linked-thread refresh shows a later Gmail reply without manual re-import
- verifies manual search override is sent as `queryOverride`
Verification:
## Verification
- frontend dependencies were installed with `npm ci` in `job-tracker-ui`
- focused React test passed:
- `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/correspondence-gmail-import.test.tsx`
Notes:
- The Gmail tab now treats the backend as the source of truth for ranking while keeping the manual search field as a fallback override.
## Verification Evidence
| # | Command | Exit Code | Verdict | Duration |
|---|---------|-----------|---------|----------|
| 1 | `npm ci` (job-tracker-ui) | 0 | ✅ pass | not recorded |
| 2 | `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/correspondence-gmail-import.test.tsx` | 0 | ✅ pass | ~2.8s |
## Diagnostics
- Open a job with imported Gmail correspondence and inspect the Gmail tab chips: linked-thread count, last refresh summary, and Gmail connection `lastSyncedAt` show whether the workspace considers the thread live.
- Watch network traffic for `GET /gmail/job-candidates` and `POST /gmail/refresh-linked-threads` to confirm the workspace distinguishes ranked suggestions from already-linked thread refresh.
- Inspect rendered correspondence chips (`Thread ...`, `From ...`, `To ...`) to verify imported Gmail metadata survived the round trip from persistence to UI.
- Read `job-tracker-ui/src/correspondence-gmail-import.test.tsx` for the durable automated proof of manual query override plus no-manual-reimport continuity.
## Notes
The Gmail tab now treats the backend as the source of truth for ranking while keeping the manual search field as a fallback override, and it refreshes known linked threads once per loaded job/thread-set automatically to avoid re-import loops.