Files
jobtrackingapp/.gsd/milestones/M001/slices/S01/S01-PLAN.md
T

7.9 KiB
Raw Blame History

S01: Smarter Gmail import and matching

Goal: Make Gmail import job-aware enough that a user reviewing one imported job sees the right candidate messages/threads first, can import them with clearer confidence signals, and ends up with job-linked correspondence that needs materially less cleanup. Demo: From a job opened in the workspace, the user connects Gmail, sees ranked thread/message suggestions built from that jobs company/recruiter context, imports either one message or a full thread, and immediately sees the imported correspondence reflected on that job without any auto-send behavior.

S01 directly advances active requirements R001 and R002, and it also lays the continuity foundation S04 will later rely on for R010. The work is split backend-first because the highest risk is the matching contract itself: if ranking, dedupe, and metadata stay client-only, the UI can look nicer while the import loop still feels untrustworthy. Once that contract exists, the frontend task can focus on presenting the ranked results cleanly instead of inventing its own heuristics.

Must-Haves

  • A job-scoped Gmail matching API ranks candidate messages/threads for a specific JobApplication using company, recruiter, title, and existing correspondence signals rather than a generic inbox search alone.
  • Gmail import persists enough metadata to preserve message/thread identity, expose already-imported state, and reduce duplicate cleanup when the user imports single messages or whole threads into a job.
  • The job workspace Correspondence tab shows ranked Gmail suggestions with match reasons/confidence, keeps a manual search override, and refreshes the job-linked correspondence list after import.

Proof Level

  • This slice proves: integration
  • Real runtime required: yes
  • Human/UAT required: yes

Verification

  • dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter GmailControllerTests
  • CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/correspondence-gmail-import.test.tsx
  • Manual UAT: run the app, connect a real Gmail account, open one job in the workspace, confirm ranked Gmail suggestions appear before manual search, import one message and one full thread, and verify the imported correspondence appears on that same job with duplicates skipped and no send/apply automation.

Observability / Diagnostics

  • Runtime signals: ranked match reasons/confidence returned per candidate, import result counts for imported/skipped messages, and existing request logs with trace ids around Gmail endpoints.
  • Inspection surfaces: api/gmail/status, the new job-scoped Gmail matching response in JobTrackerApi/Controllers/GmailController.cs, the correspondence list in job-tracker-ui/src/components/Correspondence.tsx, and backend/frontend tests covering duplicate and failure paths.
  • Failure visibility: Gmail connection state, empty/noisy candidate lists, duplicate-skip counts, and per-candidate imported/already-linked flags should remain visible enough to tell whether failure came from query construction, Gmail retrieval, dedupe, or UI wiring.
  • Redaction constraints: never expose OAuth tokens or full message bodies in logs; diagnostics should stay at message metadata, ranking reasons, and import counts.

Integration Closure

  • Upstream surfaces consumed: JobTrackerApi/Services/GmailOAuthService.cs, Data/JobTrackerContext.cs, Models/JobApplication.cs, Models/Company.cs, Models/Correspondence.cs, job-tracker-ui/src/components/JobDetailsDialog.tsx, and job-tracker-ui/src/components/Correspondence.tsx.
  • New wiring introduced in this slice: a job-aware Gmail candidate contract in the API, correspondence persistence that retains Gmail thread/message identity, and job-context-driven rendering in the workspace Gmail import UI.
  • What remains before the milestone is truly usable end-to-end: later slices still need to consume the imported correspondence for stronger drafting and daily workflow surfaces, but Gmail import itself should be trustworthy after this slice.

Tasks

  • T01: Add a job-aware Gmail matching contract and backend ranking tests est:3h
    • Why: The slice risk sits in matching quality, so the backend needs to own candidate discovery, ranking, and duplicate awareness before the UI can present trustworthy suggestions.
    • Files: JobTrackerApi/Controllers/GmailController.cs, JobTrackerApi/Services/GmailOAuthService.cs, JobTrackerApi.Tests/GmailControllerTests.cs
    • Do: Add a job-scoped Gmail candidate endpoint that loads the owned job plus company context, builds multiple Gmail queries from recruiter/company/title/correspondence signals, dedupes messages and groups them by thread, and returns ranked suggestions with match reasons, confidence inputs, and already-imported flags; cover the contract with focused controller/service tests instead of leaving ranking in Correspondence.tsx.
    • Verify: dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter GmailControllerTests
    • Done when: the API can return ranked Gmail candidates for one job with explicit reasons and duplicate state, and backend tests prove owned-job lookup plus ranking/dedupe behavior.
  • T02: Persist Gmail thread metadata and harden import continuity est:3h
    • Why: Smarter matching is not enough if imported correspondence still loses thread identity or forces downstream slices to re-derive sender/thread information later.
    • Files: Models/Correspondence.cs, JobTrackerApi/Controllers/GmailController.cs, JobTrackerApi/Controllers/CorrespondenceController.cs, JobTrackerApi/Program.cs, JobTrackerApi.Tests/GmailControllerTests.cs
    • Do: Extend correspondence persistence and import logic so Gmail imports store thread/message identity plus sender/recipient metadata, update import responses and duplicate handling to reflect imported vs skipped outcomes clearly, and update startup schema guards so older SQLite/MySQL dev databases remain bootable when the new fields land.
    • Verify: dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter GmailControllerTests
    • Done when: imported correspondence keeps enough Gmail metadata for thread continuity and dedupe, compatibility guards are planned with the schema change, and tests prove single-message/thread imports behave correctly on repeat imports.
  • T03: Wire ranked Gmail suggestions into the job workspace UI est:3h
    • Why: The slice is only complete when the user can act on the smarter backend contract inside the actual job workspace rather than through a generic inbox search UI.
    • Files: job-tracker-ui/src/components/JobDetailsDialog.tsx, job-tracker-ui/src/components/Correspondence.tsx, job-tracker-ui/src/types.ts, job-tracker-ui/src/correspondence-gmail-import.test.tsx
    • Do: Pass job context from JobDetailsDialog into Correspondence, replace client-side primary ranking with the server-provided candidate contract, show match reasons/confidence/import state and thread actions, keep manual query/search as a fallback override, and add a React test that proves ranked suggestions and import refresh behavior in the dialog.
    • Verify: CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/correspondence-gmail-import.test.tsx
    • Done when: the Correspondence Gmail tab opens on ranked job-aware suggestions, still supports manual search overrides, refreshes the correspondence list after import, and the new UI test passes.

Files Likely Touched

  • JobTrackerApi/Controllers/GmailController.cs
  • JobTrackerApi/Services/GmailOAuthService.cs
  • Models/Correspondence.cs
  • JobTrackerApi/Controllers/CorrespondenceController.cs
  • JobTrackerApi/Program.cs
  • JobTrackerApi.Tests/GmailControllerTests.cs
  • job-tracker-ui/src/components/JobDetailsDialog.tsx
  • job-tracker-ui/src/components/Correspondence.tsx
  • job-tracker-ui/src/types.ts
  • job-tracker-ui/src/correspondence-gmail-import.test.tsx