# 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 `<<>>` 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 components** — `DashboardView.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 |