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

9.3 KiB
Raw Blame History

id, parent, milestone, provides, affects, key_files, key_decisions, patterns_established, observability_surfaces, drill_down_paths, duration, verification_result, completed_at
id parent milestone provides affects key_files key_decisions patterns_established observability_surfaces drill_down_paths duration verification_result completed_at
S01 M001 M001
Job-scoped Gmail matching and import now run from the job workspace with backend-owned ranking reasons, duplicate-aware import contracts, persisted Gmail thread metadata, and linked-thread refresh that imports later replies into the same job.
S02
S03
S04
S05
JobTrackerApi/Controllers/GmailController.cs
JobTrackerApi/Services/GmailOAuthService.cs
JobTrackerApi.Tests/GmailControllerTests.cs
Models/Correspondence.cs
JobTrackerApi/Controllers/CorrespondenceController.cs
job-tracker-ui/src/components/Correspondence.tsx
job-tracker-ui/src/components/JobDetailsDialog.tsx
job-tracker-ui/src/types.ts
job-tracker-ui/src/correspondence-gmail-import.test.tsx
Keep Gmail continuity bounded to known `ExternalThreadId` values for one job via `POST /api/gmail/refresh-linked-threads` instead of inbox-wide Gmail watch/history infrastructure.
Treat the backend as the source of truth for Gmail candidate ranking, duplicate visibility, and linked-thread refresh counts so the workspace UI stays explanatory without re-implementing Gmail heuristics in React.
Gmail-derived correspondence is first-class job history
imported rows persist external message/thread ids plus sender/recipient labels and can be rendered directly in the timeline/workspace.
Linked-thread continuity is pull-based and duplicate-safe
refresh reads already-linked thread ids for one owned job, skips known external message ids, and imports only new Gmail replies into that same job.
The workspace distinguishes ranked import suggestions from already-linked live threads, with automatic one-shot refresh per loaded job/thread-set and an explicit manual refresh action.
GET /api/gmail/status
GET /api/gmail/job-candidates
POST /api/gmail/import
POST /api/gmail/import-thread
POST /api/gmail/refresh-linked-threads
persisted Correspondence.ExternalMessageId / ExternalThreadId / ExternalFrom / ExternalTo
JobTrackerApi.Tests/GmailControllerTests.cs
job-tracker-ui/src/correspondence-gmail-import.test.tsx
.gsd/milestones/M001/slices/S01/tasks/T01-SUMMARY.md
.gsd/milestones/M001/slices/S01/tasks/T02-SUMMARY.md
.gsd/milestones/M001/slices/S01/tasks/T03-SUMMARY.md
~1 slice closure session + prior executor task work passed 2026-03-24T11:54:52+01:00

S01: Smarter Gmail import and matching

Outcome

S01 now delivers a job-aware Gmail import loop that is materially closer to the milestone trust bar than the pre-slice state. The workspace can:

  • connect Gmail and load ranked candidate messages/threads for one owned job
  • explain why each Gmail candidate matched via score/confidence/match reasons
  • import either a single message or an entire thread with duplicate-safe result counts
  • persist Gmail thread identity plus raw sender/recipient labels on correspondence rows
  • refresh already-linked Gmail threads for that same job and import only later unseen replies
  • surface that linked-thread state back in the workspace without making the user manually re-import the thread

The biggest slice change is that Gmail import is no longer just “pick a message and save a snapshot.” It is now a bounded continuity loop around stored ExternalThreadId values.

What actually shipped

Backend contract

JobTrackerApi/Controllers/GmailController.cs and JobTrackerApi/Services/GmailOAuthService.cs now cover three distinct Gmail workspace behaviors:

  1. Job-aware candidate ranking via GET /api/gmail/job-candidates

    • backend aggregates Gmail hits per job query
    • duplicate Gmail hits are merged server-side
    • response includes weighted match reasons, matched queries, imported flags, confidence, and thread/message counts
  2. Duplicate-safe import via POST /api/gmail/import and POST /api/gmail/import-thread

    • single-message imports return Imported/Skipped plus the imported or existing correspondence row
    • thread imports report imported/skipped counts instead of silently duplicating rows
    • imported correspondence persists ExternalMessageId, ExternalThreadId, ExternalFrom, and ExternalTo
  3. Linked-thread continuity via POST /api/gmail/refresh-linked-threads

    • reads the owned jobs existing linked Gmail thread ids
    • fetches those threads from Gmail
    • skips already-known external message ids
    • imports only new messages into the same job
    • returns refresh counts per job and per thread
    • distinguishes invalid job, disconnected Gmail, empty linked-thread set, and already-current outcomes

Persistence and compatibility

Models/Correspondence.cs, JobTrackerApi/Controllers/CorrespondenceController.cs, and JobTrackerApi/Program.cs now treat Gmail metadata as part of the durable correspondence model, including compatibility guards for the extra columns.

Workspace UI

job-tracker-ui/src/components/Correspondence.tsx now makes the job workspace the real Gmail import surface instead of a generic search-first flow.

It now:

  • loads backend-ranked Gmail candidates for the current job
  • shows confidence/score/match reasons/already-linked status
  • preserves manual query override through the same endpoint
  • renders persisted Gmail thread/from/to metadata in the correspondence list
  • automatically refreshes linked Gmail threads once per loaded job/thread-set
  • exposes a manual “Refresh linked threads” action for another bounded pull in the same session
  • updates the workspace after message import, thread import, and linked-thread refresh

job-tracker-ui/src/correspondence-gmail-import.test.tsx now covers both the ranked import path and the no-manual-reimport continuity path.

Verification run in this closure session

Automated checks

Command Result
dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter GmailControllerTests passed
CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/correspondence-gmail-import.test.tsx passed
dotnet build JobTrackerApi/JobTrackerApi.csproj passed

The backend verification command originally described in project knowledge was blocked by unrelated test-project drift earlier in the milestone, but that drift was fixed during this closure pass, and the exact filtered command now passes.

Observability confirmed

The slices durable inspection surfaces are now coherent:

  • GET /api/gmail/status exposes Gmail connection freshness (lastSyncedAt)
  • GET /api/gmail/job-candidates exposes job-scoped ranking details and duplicate visibility
  • POST /api/gmail/refresh-linked-threads exposes refresh counts and per-thread status
  • persisted correspondence rows carry external Gmail thread/message/from/to metadata
  • focused backend/frontend tests encode the intended behavior for future refactors

Human / live Gmail UAT status

This auto-mode session did not have a live Gmail account wired for a real-account browser pass, so the required human/live UAT was prepared as a concrete script in S01-UAT.md rather than executed here. The implementation and automated slice gates passed; the live-account trust check remains a human runbook item.

Requirement impact

  • R002 moved to validated based on the shipped linked-thread refresh contract plus focused backend/frontend verification.
  • R010 remains active, but S01 materially advances it by making Gmail correspondence continuity part of the same job history instead of a one-off import snapshot.

Key downstream implications

For S02

Imported Gmail correspondence is now trustworthy enough to use as drafting context. Downstream draft generation should consume job-linked correspondence rows directly, including sender/recipient/thread metadata when useful for tone or recipient context.

For S03

Reply/follow-up drafting can now assume two important invariants:

  • Gmail correspondence is attached to a specific job
  • later Gmail replies can be pulled into that same job without user re-import

That means reply/follow-up context assembly should build from the persisted job correspondence set, not from transient Gmail candidate data.

For S04/S05

Daily-loop surfaces can now rely on a clearer distinction between:

  • jobs that only have candidate Gmail matches
  • jobs with already-linked live Gmail threads
  • jobs whose linked threads were refreshed and imported new correspondence

Those are useful action/readiness signals for dashboards, follow-up surfaces, and final end-to-end trust checks.

Notable lessons / non-obvious details

  • The bounded sync model is deliberate: refresh is over known linked thread ids for one job, not inbox-wide Gmail watch/history state.
  • The React workspace auto-refresh is intentionally one-shot per jobId + linked thread set; repeated pulls in the same session require the explicit refresh action.
  • The filtered Gmail backend test command compiles the whole JobTrackerApi.Tests project before filtering, so unrelated test drift can still block slice verification if future work breaks test signatures again.

Slice verdict

S01 now establishes the milestones Gmail foundation: smarter matching, clearer import trust signals, persisted Gmail metadata, and real linked-thread continuity in the job workspace.