Files

11 KiB

S05 — Research

Date: 2026-03-24

Summary

S05 is an integration-and-polish slice, not a new subsystem. The codebase already has the main milestone pieces in place: job-table/dashboard routing into one workspace (job-tracker-ui/src/jobWorkspaceRoute.ts), Gmail import plus linked-thread refresh (job-tracker-ui/src/components/Correspondence.tsx, JobTrackerApi/Controllers/GmailController.cs), saved application-package drafting (job-tracker-ui/src/components/JobDetailsDialog.tsx, JobTrackerApi/Controllers/JobApplicationsController.cs), and grounded follow-up drafting with explicit manual send (JobDetailsDialog.tsx, JobApplicationsController.cs). What is still missing is one trustworthy proof path and a small amount of glue cleanup so the full loop feels coherent instead of slice-by-slice.

The active requirements this slice most directly supports are R008 and R010. R008 matters because POST /api/jobapplications/{id}/send-followup really calls _email.SendAsync(...), so final verification must preserve the explicit-user-action boundary and avoid any accidental live outbound send during UAT. R010 matters because the continuity story is currently spread across several surfaces: reminders/dashboard route by followUpReason text, the table uses tailoredCvText plus notes as a package-readiness proxy, Gmail continuity is one-shot auto-refresh plus manual refresh, and follow-up grounding comes from a separate DTO. S05 should make those surfaces feel like one loop and prove them together.

Recommendation

Treat S05 as “integrated regression first, then polish only what the integrated proof exposes.” Do not invent a new workflow. Reuse the existing shared /jobs?open=...&tab=...&followMode=... entry pattern, and keep backend DTOs as the source of truth instead of adding more browser-side heuristics.

Two loaded skills reinforce that approach:

  • react-best-practices: keep derived UI state in helpers instead of adding more mirrored effect-driven state (rerender-derived-state-no-effect, rerender-dependencies), and avoid introducing new fetch waterfalls when composing the final loop (async-parallel).
  • aspnet-core: keep the controller/API contract as the authoritative feature seam. If S05 needs clearer trust/readiness signals, add them in JobApplicationsController DTOs/endpoints instead of duplicating string parsing rules in multiple React components.

Primary recommendation: add one end-to-end UI regression that spans table/dashboard/reminders → shared workspace → package save → Gmail correspondence continuity → follow-up draft/manual-send boundary, then patch any trust gaps that test exposes. That is the fastest path to milestone-level confidence.

Implementation Landscape

Key Files

  • job-tracker-ui/src/jobWorkspaceRoute.ts — single shared route builder for opening a job workspace on a specific tab/mode. This is the seam S04 already established; S05 should keep using it rather than creating new navigation paths.
  • job-tracker-ui/src/components/JobTable.tsx — primary entry surface. Important details:
    • urgency/action chips route into the shared workspace
    • getActionSignals() is the current place where “what needs attention now” is inferred
    • readinessFilter === "needs-work" currently uses !job.tailoredCvText || !job.notes, which is a coarse proxy because notes also holds the S02 application-answer marker block and arbitrary notes
  • job-tracker-ui/src/components/DashboardView.tsx — reminder/attention overview. It routes into Tailored CV or Follow-up based on followUpReason string matching. Useful for integrated proof, but fragile if more action types appear.
  • job-tracker-ui/src/components/RemindersView.tsx — same pattern as dashboard: groups items by followUpReason text and routes into the shared workspace.
  • job-tracker-ui/src/components/JobDetailsDialog.tsx — the real job workspace. It already contains the critical integrated loop pieces:
    • package generation/save in tab 3
    • follow-up draft fetch + editable draft + explicit send/log in tab 4
    • readiness in tab 8
    • lazy per-tab data loading
  • job-tracker-ui/src/components/Correspondence.tsx — Gmail import/continuity surface. Key non-obvious behavior:
    • auto-refresh of linked threads is one-shot per jobId + linkedThreadIds set via autoRefreshKeyRef
    • repeated pulls in the same session require the explicit Refresh linked threads action
    • imported correspondence rows surface Gmail metadata directly in the timeline area
  • job-tracker-ui/src/daily-control-loop.test.tsx — current best overview-surface proof. Verifies dashboard/reminders/job-table route into the shared workspace, but stops short of the full import/package/follow-up loop.
  • job-tracker-ui/src/correspondence-gmail-import.test.tsx — current best Gmail continuity proof. Verifies ranked Gmail import and linked-thread refresh behavior, including the later reply appearing without manual re-import.
  • job-tracker-ui/src/job-details-generated-drafts.test.tsx — best proof for package generate/edit/save/reload.
  • job-tracker-ui/src/job-details-followup-drafts.test.tsx — best proof for follow-up grounding and the manual-send boundary.
  • JobTrackerApi/Controllers/JobApplicationsController.cs — backend source of truth for S05 trust signals:
    • GET /api/jobapplications/{id}/followup-draft
    • POST /api/jobapplications/{id}/send-followup
    • GET /api/jobapplications/{id}/readiness
    • PUT /api/jobapplications/{id}/application-drafts
    • POST /api/jobapplications/{id}/generate-application-package
    • also owns reminders/analytics/readiness logic used by the overview surfaces
  • JobTrackerApi/Controllers/GmailController.cs — backend source of truth for Gmail match/import/refresh continuity.
  • JobTrackerApi/Services/FollowUpReminderHostedService.cs — sends reminder emails to the app user with a deep link into the follow-up tab. This is not recruiter auto-send, but it matters for live verification because outbound email infrastructure may be active.
  • job-tracker-ui/src/api.ts — local UI targets http://localhost:5202/api on localhost; browser UAT must run the backend too.

Build Order

  1. Prove the whole loop in one focused UI regression before polishing.

    • Best seam: add a new integrated React test or extend src/daily-control-loop.test.tsx.
    • It should cover: open from an overview surface → land on workspace tab → generate/save package → confirm saved state is reused → open/import/refresh correspondence → generate follow-up with grounding → verify send remains explicit/manual.
    • This gives the planner one artifact that tells it exactly what still feels fragmented.
  2. Then fix trust/continuity heuristics exposed by that integrated proof. Likely candidates from current code:

    • replace/centralize string-matching action routing (followUpReason.includes('tailored cv')) if it causes ambiguous or brittle behavior across DashboardView.tsx and RemindersView.tsx
    • tighten package-readiness/action inference in JobTable.tsx so it reflects saved package state more directly than !job.notes
    • if overview surfaces need richer trust signals, prefer adding explicit API fields in JobApplicationsController.cs over duplicating UI inference
  3. Only after the integrated regression passes, do final browser UAT on the real app path.

    • Use the existing S04 pattern: browser entry through the real /jobs / /dashboard surfaces, not component-only proof.
    • Keep live send safe: do not rely on clicking the final send button unless the environment is configured to a safe sink/stub recipient.

Verification Approach

Automated regression set already worth keeping:

  • dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter GmailControllerTests
  • dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsApplicationPackageTests
  • dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsFollowUpDraftTests
  • CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/correspondence-gmail-import.test.tsx
  • CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-generated-drafts.test.tsx
  • CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-followup-drafts.test.tsx
  • CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/daily-control-loop.test.tsx
  • add one new S05 integrated UI test and run it directly by path
  • dotnet build JobTrackerApi/JobTrackerApi.csproj
  • CI=true npm --prefix job-tracker-ui run build

Browser/UAT proof should explicitly confirm:

  • job table/dashboard/reminders all open the same workspace model
  • package work saved in Tailored CV is visible when follow-up drafting runs later
  • Gmail linked-thread refresh updates correspondence without manual re-import of the whole thread
  • follow-up drafting shows grounding from saved package + correspondence context
  • no outbound recruiter email is sent without the explicit send action

Constraints

  • Local browser verification requires both frontend and backend. job-tracker-ui/src/api.ts hard-codes http://localhost:5202/api on localhost.
  • send-followup is a real email-sending endpoint; final verification must preserve R008 by avoiding accidental live sends.
  • Gmail thread auto-refresh in Correspondence.tsx is intentionally one-shot per linked-thread set. A second refresh in the same session needs the manual button; that is by design, not a bug.
  • The S02 application-answer draft still lives inside JobApplication.Notes with the <<<APPLICATION_ANSWER_DRAFT>>> marker block; any S05 readiness logic must not treat all notes as generic free text.

Common Pitfalls

  • Treating notes as generic readiness state — in this milestone, notes may contain the persisted application-answer draft block. If S05 wants a better package/trust signal, do not keep leaning on !job.notes alone.
  • Duplicating action inference in multiple componentsDashboardView.tsx, RemindersView.tsx, and JobTable.tsx already infer next actions separately. If S05 needs to change action logic, centralize it in a helper or API contract instead of drifting three copies.
  • Mistaking manual-send for no-send — R008 forbids autonomous outbound communication, not explicit user-triggered send. S05 should verify the manual boundary remains explicit, not remove the send capability.
  • Running browser UAT with only the frontend — the UI will render but fail to load real data because localhost calls go to http://localhost:5202/api.

Open Risks

  • The biggest remaining risk is not missing code, but a fragmented trust story: overview surfaces, package workspace, Gmail continuity, and follow-up grounding may all work individually while still feeling loosely connected. The integrated UI regression should be used to decide whether S05 needs a data-contract tweak or just copy/UX polish.
  • If live email infrastructure is enabled, naive UAT on send-followup could send a real message. Prefer a safe sink/stubbed mail setup for final acceptance.

Skills Discovered

Technology Skill Status
React UI workflow / performance react-best-practices available
ASP.NET Core controller APIs aspnet-core available