diff --git a/.gsd/DECISIONS.md b/.gsd/DECISIONS.md index bd81d82..24b8113 100644 --- a/.gsd/DECISIONS.md +++ b/.gsd/DECISIONS.md @@ -15,3 +15,4 @@ | D007 | M001/S01 | gmail-continuity | What “good Gmail import” now means for M001 | Treat Gmail import as full-thread continuity: the user must be able to import the whole relevant thread, and already-linked Gmail threads must refresh automatically so later inbound messages and user-sent replies appear on the job without manual re-import. This supersedes the narrower one-time-import interpretation inside D005’s Gmail-import focus. | The user explicitly asked whether the app can bring the whole email thread and automatically show their reply later without re-pulling/importing again. That changes the trust bar from “find and import the right message” to “keep the linked thread current over time,” while still preserving the no-auto-send boundary from D002. | Yes — if Gmail API or product constraints later require a different sync model | human | | D008 | M001/S01 planning | gmail-sync | How linked Gmail threads stay current in S01 | Use a job-scoped refresh flow over already-imported Gmail thread IDs, triggered from the job workspace/API, instead of building inbox-wide Gmail watch/history cursor infrastructure in M001. | The codebase already persists ExternalThreadId per correspondence and lacks Gmail history/watch infrastructure. Fetching known linked threads for one job keeps scope bounded, fits the single-user workspace model, supports duplicate-safe import of new inbound and sent replies, and creates a trustworthy continuity loop without adding brittle webhook/cursor state before the milestone proves value. | Yes — if real usage shows job-scoped pull refresh is too slow or misses important continuity cases. | agent | | D009 | M001/S01 closure | gmail-matching | Where Gmail candidate aggregation and ranking logic should live for job-aware import | Keep Gmail query-hit aggregation, dedupe, matched-query traces, and ranking reasons in the backend contract instead of recreating that logic in the React workspace. | The correspondence workspace needs explanatory candidate ranking plus duplicate visibility, and putting the logic in the API keeps one source of truth for scoring/import state while preventing browser-side heuristic drift. | Yes | agent | +| D010 | M001/S03 closeout | followup-drafting | How follow-up grounding should be exposed to the workspace | Return explicit follow-up grounding fields (`contextSummary`, `contextSignals`, `threadSubject`, and last-correspondence metadata) from the backend DTO instead of making the React workspace infer them client-side. | The slice needed draft trust, not just draft text. Putting grounding signals in the API contract gives the UI a durable explanation surface, keeps thread/package inference in one place with generation logic, and makes backend/frontend tests assert the same source of truth. | Yes | agent | diff --git a/.gsd/KNOWLEDGE.md b/.gsd/KNOWLEDGE.md index 4397ec3..f4e122e 100644 --- a/.gsd/KNOWLEDGE.md +++ b/.gsd/KNOWLEDGE.md @@ -4,3 +4,4 @@ - The correspondence workspace auto-refreshes linked Gmail threads once per `jobId + ExternalThreadId set` using `POST /api/gmail/refresh-linked-threads`; if you need another pull in the same UI session without changing linked threads, use the explicit "Refresh linked threads" action. - The S02 package workspace persists the application-answer draft inside `JobApplication.Notes` using the marker block `<<>> ... <<>>`; downstream slices should replace or parse that block instead of appending free-form notes. - `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsApplicationPackageTests` is now a trustworthy direct verification command in this worktree for package-generation and notes-replacement behavior; prefer it over older isolated-harness guidance when checking S02 regressions. +- `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsFollowUpDraftTests` is now trustworthy again in this worktree after restoring the missing ASP.NET Core / Identity / xUnit test-project references in `JobTrackerApi.Tests/JobTrackerApi.Tests.csproj`; older task notes that require an isolated Docker harness are stale. diff --git a/.gsd/PROJECT.md b/.gsd/PROJECT.md index 81ef4c4..9ef6747 100644 --- a/.gsd/PROJECT.md +++ b/.gsd/PROJECT.md @@ -10,7 +10,7 @@ The product must let one person run a real job search without losing the thread: ## Current State -A substantial brownfield app already exists. The repo has a React frontend, an ASP.NET Core API, and a local FastAPI AI service. Current capabilities already include job tracking, companies, attachments, correspondence, reminders, job import preview, Gmail connection/import, profile CV upload/parsing/rewrite flows, AI-assisted tailored CV and cover-letter generation, candidate-fit/focus-plan/interview-prep/readiness endpoints, and dashboard/system surfaces. S01 is complete: the Gmail workspace is job-aware, backend ranking happens server-side, Gmail imports persist thread/from/to metadata, duplicate-safe single-message and thread imports are explicit, and already-linked Gmail threads refresh back into the same job automatically via a bounded `ExternalThreadId` pull. S02 is now complete as well: backend package generation consumes imported correspondence plus recruiter/job state, and the Tailored CV tab treats tailored CV, cover letter, recruiter message, and saved application-answer material as one editable workspace tied back to the job instead of a throwaway preview pane. The next planned phase is S03, which will consume the saved package plus imported correspondence to generate grounded follow-up and reply drafts before later slices reconnect the daily control loop and re-run end-to-end trust checks. +A substantial brownfield app already exists. The repo has a React frontend, an ASP.NET Core API, and a local FastAPI AI service. Current capabilities already include job tracking, companies, attachments, correspondence, reminders, job import preview, Gmail connection/import, profile CV upload/parsing/rewrite flows, AI-assisted tailored CV and cover-letter generation, candidate-fit/focus-plan/interview-prep/readiness endpoints, and dashboard/system surfaces. S01 is complete: the Gmail workspace is job-aware, backend ranking happens server-side, Gmail imports persist thread/from/to metadata, duplicate-safe single-message and thread imports are explicit, and already-linked Gmail threads refresh back into the same job automatically via a bounded `ExternalThreadId` pull. S02 is complete: backend package generation consumes imported correspondence plus recruiter/job state, and the Tailored CV tab treats tailored CV, cover letter, recruiter message, and saved application-answer material as one editable workspace tied back to the job instead of a throwaway preview pane. S03 is now complete as well: follow-up drafting reuses imported correspondence plus saved package material, the Follow-up tab shows why the draft was generated and what thread/package context informed it, and the user must still manually send/log any outbound message. The next planned phase is S04, which will reconnect the daily control loop through the table/dashboard surfaces before S05 re-validates the end-to-end trust loop. ## Architecture / Key Patterns diff --git a/.gsd/REQUIREMENTS.md b/.gsd/REQUIREMENTS.md index 683dcd1..b6cfc52 100644 --- a/.gsd/REQUIREMENTS.md +++ b/.gsd/REQUIREMENTS.md @@ -12,8 +12,8 @@ This file is the explicit capability and coverage contract for the project. - Source: user - Primary owning slice: M001/S03 - Supporting slices: M001/S05 -- Validation: mapped -- Notes: This is a durable trust constraint across all milestones. +- Validation: Constrained by S03 follow-up tests and workspace UX: focused backend/frontend verification proves drafting uses context while outbound follow-up still requires an explicit manual send/log action. +- Notes: S03 re-checked the manual-send boundary inside the follow-up workspace: users edit drafts locally and `POST /api/jobapplications/{id}/send-followup` fires only from the explicit send/log action. The broader anti-autonomy constraint still remains active across later milestone validation. ### R009 — Core UX, data model emphasis, and roadmap decisions should optimize for one person managing their own search. - Class: constraint @@ -208,7 +208,7 @@ This file is the explicit capability and coverage contract for the project. | R005 | continuity | validated | M001/S04 | M001/S05 | Validated by M001/S04: the job table now exposes actionable urgency chips that route directly into the relevant job workspace tab, focused daily-loop UI tests pass, and browser verification confirmed the table/dashboard/reminders flow routes into the same workspace model. | | R006 | continuity | validated | M001/S04 | M001/S05 | Validated by M001/S04: dashboard and reminders now expose actionable attention items routed into the existing job workspace, focused daily-loop UI tests pass, and browser verification confirmed those surfaces open the correct job workspace state. | | R007 | core-capability | validated | M001/S03 | M001/S02, M001/S04 | Validated by M001/S03 and M001/S04: the per-job workspace now supports imported correspondence review, package drafting, follow-up drafting, and routed entry from overview surfaces, with focused tests and browser verification covering package and follow-up loops. | -| R008 | constraint | active | M001/S03 | M001/S05 | mapped | +| R008 | constraint | active | M001/S03 | M001/S05 | Constrained by S03 follow-up tests and workspace UX: focused backend/frontend verification proves drafting uses context while outbound follow-up still requires an explicit manual send/log action. | | R009 | constraint | active | M001/S04 | M002/S01, M004/S01 | mapped | | R010 | continuity | active | M001/S04 | M001/S01, M001/S03, M001/S05 | mapped | | R011 | operability | deferred | M002/S02 | none | unmapped | diff --git a/.gsd/journal/2026-03-24.jsonl b/.gsd/journal/2026-03-24.jsonl index 15a44e6..11c224d 100644 --- a/.gsd/journal/2026-03-24.jsonl +++ b/.gsd/journal/2026-03-24.jsonl @@ -38,3 +38,8 @@ {"ts":"2026-03-24T11:27:04.984Z","flowId":"1e956ae8-b804-4454-8fba-0d7983c8f804","seq":2,"eventType":"dispatch-match","rule":"summarizing → complete-slice","data":{"unitType":"complete-slice","unitId":"M001/S02"}} {"ts":"2026-03-24T11:27:04.991Z","flowId":"1e956ae8-b804-4454-8fba-0d7983c8f804","seq":3,"eventType":"unit-start","data":{"unitType":"complete-slice","unitId":"M001/S02"}} {"ts":"2026-03-24T11:32:21.479Z","flowId":"1e956ae8-b804-4454-8fba-0d7983c8f804","seq":4,"eventType":"unit-end","data":{"unitType":"complete-slice","unitId":"M001/S02","status":"completed","artifactVerified":true},"causedBy":{"flowId":"1e956ae8-b804-4454-8fba-0d7983c8f804","seq":3}} +{"ts":"2026-03-24T11:32:22.057Z","flowId":"1e956ae8-b804-4454-8fba-0d7983c8f804","seq":5,"eventType":"iteration-end","data":{"iteration":5}} +{"ts":"2026-03-24T11:32:22.059Z","flowId":"adff88bd-a004-44ee-8707-a92cf11b993c","seq":1,"eventType":"iteration-start","data":{"iteration":6}} +{"ts":"2026-03-24T11:32:22.159Z","flowId":"adff88bd-a004-44ee-8707-a92cf11b993c","seq":2,"eventType":"dispatch-match","rule":"summarizing → complete-slice","data":{"unitType":"complete-slice","unitId":"M001/S03"}} +{"ts":"2026-03-24T11:32:22.165Z","flowId":"adff88bd-a004-44ee-8707-a92cf11b993c","seq":3,"eventType":"unit-start","data":{"unitType":"complete-slice","unitId":"M001/S03"}} +{"ts":"2026-03-24T11:40:09.423Z","flowId":"adff88bd-a004-44ee-8707-a92cf11b993c","seq":4,"eventType":"unit-end","data":{"unitType":"complete-slice","unitId":"M001/S03","status":"completed","artifactVerified":true},"causedBy":{"flowId":"adff88bd-a004-44ee-8707-a92cf11b993c","seq":3}} diff --git a/.gsd/milestones/M001/M001-ROADMAP.md b/.gsd/milestones/M001/M001-ROADMAP.md index d72d0dd..bf8c1e3 100644 --- a/.gsd/milestones/M001/M001-ROADMAP.md +++ b/.gsd/milestones/M001/M001-ROADMAP.md @@ -58,7 +58,7 @@ This milestone is complete only when all are true: - [x] **S02: Stronger AI application package drafting** `risk:high` `depends:[S01]` > After this: From an imported job plus profile/CV context, the app generates materially better tailored CV and cover-letter drafts that feel specific and usable. -- [ ] **S03: Reply and follow-up drafting from real thread context** `risk:medium` `depends:[S01,S02]` +- [x] **S03: Reply and follow-up drafting from real thread context** `risk:medium` `depends:[S01,S02]` > After this: Inside a job, the user can generate follow-up and reply drafts grounded in imported and automatically refreshed correspondence plus saved application context, then edit them before sending manually. - [ ] **S04: Daily control loop surfaces** `risk:medium` `depends:[S01,S03]` diff --git a/.gsd/milestones/M001/slices/S03/S03-SUMMARY.md b/.gsd/milestones/M001/slices/S03/S03-SUMMARY.md index ae92f81..f43be74 100644 --- a/.gsd/milestones/M001/slices/S03/S03-SUMMARY.md +++ b/.gsd/milestones/M001/slices/S03/S03-SUMMARY.md @@ -1,34 +1,107 @@ --- -title: S03 summary -status: done -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/followuptests/FollowUpTests.csproj...' - - 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 run build - - Browser UAT on built branch UI via localhost:3001 against localhost:5202 +id: S03 +parent: M001 +milestone: M001 +provides: + - Context-grounded follow-up drafting that combines imported correspondence, saved application package material, and explicit manual send/log behavior inside the job workspace. +requires: + - slice: S01 + provides: Imported correspondence records, linked Gmail thread metadata, and ongoing thread refresh continuity tied to a job. + - slice: S02 + provides: Saved tailored CV, cover letter, recruiter message, and marker-delimited application-answer draft state reused by follow-up drafting. +affects: + - S04 +key_files: + - JobTrackerApi/Controllers/JobApplicationsController.cs + - JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs + - JobTrackerApi.Tests/JobTrackerApi.Tests.csproj + - job-tracker-ui/src/components/JobDetailsDialog.tsx + - job-tracker-ui/src/types.ts + - job-tracker-ui/src/job-details-followup-drafts.test.tsx +key_decisions: + - Expose follow-up grounding (`contextSummary`, `contextSignals`, thread subject, and last-correspondence metadata) directly from the backend DTO so the workspace explains why the draft exists instead of inferring context client-side. + - Preserve the manual-send boundary by keeping follow-up generation separate from explicit send/log submission. +patterns_established: + - Assemble thread/package grounding once in the backend and return it as part of the draft contract, then render the same grounding verbatim in the workspace. + - Reuse the S02 notes marker block for saved application-answer context instead of inventing a second persistence path for follow-up drafting. +observability_surfaces: + - GET /api/jobapplications/{id}/followup-draft + - POST /api/jobapplications/{id}/send-followup + - GET /api/correspondence/{jobId} + - JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs + - job-tracker-ui/src/job-details-followup-drafts.test.tsx +drill_down_paths: + - .gsd/milestones/M001/slices/S03/tasks/T01-SUMMARY.md + - .gsd/milestones/M001/slices/S03/tasks/T02-SUMMARY.md +duration: 2-task slice; planned 8h implementation plus closeout verification +verification_result: passed +completed_at: 2026-03-24 --- -S03 turned follow-up drafting into a real thread-aware workspace flow. +# S03: Reply and follow-up drafting from real thread context -Delivered: -- stronger backend follow-up draft context in `JobTrackerApi/Controllers/JobApplicationsController.cs` - - imported correspondence, recruiter details, saved package material, and saved application-answer content now inform the follow-up draft - - follow-up responses expose context summary, context signals, thread subject, and last-correspondence metadata for the workspace - - reply-style subjects now reuse the active thread where available -- focused backend proof in `JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs` - - verifies the follow-up draft reacts to imported thread and saved package state -- stronger Follow-up tab in `job-tracker-ui/src/components/JobDetailsDialog.tsx` - - surfaces why the draft exists now - - shows thread/package grounding explicitly - - keeps manual-send behavior clear and editable -- focused frontend proof in `job-tracker-ui/src/job-details-followup-drafts.test.tsx` - - verifies thread grounding, editable draft state, and manual send/log behavior -- runtime browser proof - - built branch UI served locally on `localhost:3001` - - logged into the local app, opened the seeded job, verified the Follow-up tab showed thread/package grounding, sent a manual follow-up, and confirmed it logged back into Correspondence +**Follow-up drafting now reuses imported thread history plus saved package material, shows that grounding in the job workspace, and keeps outbound email behind an explicit manual send/log action.** -Net effect: -- imported correspondence and saved package material now feed useful follow-up drafting instead of sitting in separate surfaces -- the manual-send boundary remains intact -- the job workspace can now carry the user from imported thread context to reviewed draft to logged outbound follow-up +## What Happened + +S03 closed the gap between the correspondence workspace built in S01 and the saved application package workspace built in S02. Before this slice, follow-up generation mostly behaved like a generic prompt over job summary text. After this slice, `JobApplicationsController` assembles follow-up context from the latest imported correspondence, recruiter details, saved tailored CV / cover letter / recruiter message material, and the marker-delimited application-answer draft saved in notes. The API now returns not only a draft subject/body, but also the grounding metadata the UI needs to explain why the draft was generated and what informed it. + +On the frontend, `JobDetailsDialog.tsx` now treats follow-up drafting as part of the same per-job working loop rather than a blind compose form. The Follow-up tab shows thread subject, latest sender, context summary, and context signals; keeps the body editable before send; and makes the manual-send boundary explicit in the helper text and button flow. The UI posts the edited draft through the existing send/log endpoint instead of silently dispatching anything. + +During closeout, the slice-level backend verification command in the plan was restored as a first-class path by repairing missing framework/package references in `JobTrackerApi.Tests/JobTrackerApi.Tests.csproj`. That means future agents can use the filtered `dotnet test ... --filter JobApplicationsFollowUpDraftTests` command directly in this worktree instead of relying on the older isolated harness workaround recorded during task execution. + +Net effect: imported correspondence, saved package drafts, and the follow-up compose/send-log loop now form one coherent job-level workspace path. S04 can build overview and urgency surfaces on top of a follow-up flow that is already grounded and manually controlled. + +## Verification + +- Passed: `$HOME/.dotnet/dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsFollowUpDraftTests` +- Passed: `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-followup-drafts.test.tsx` +- Passed: `$HOME/.dotnet/dotnet build JobTrackerApi/JobTrackerApi.csproj` +- Passed: `CI=true npm --prefix job-tracker-ui run build` +- Observability surfaces confirmed through code/test contracts: + - `GET /api/jobapplications/{id}/followup-draft` now exposes grounding metadata (`contextSummary`, `contextSignals`, thread subject, last-correspondence details) + - `POST /api/jobapplications/{id}/send-followup` remains the explicit manual send/log boundary + - `GET /api/correspondence/{jobId}` remains the authoritative timeline surface for confirming logged outbound follow-up entries + +## New Requirements Surfaced + +- none + +## Deviations + +The written plan expected the filtered backend command to be the slice-level proof path, but task execution had fallen back to an isolated harness because `JobTrackerApi.Tests.csproj` was missing required ASP.NET Core / Identity / xUnit references. Closeout repaired the test project so the plan-level verification command now passes directly in this worktree. + +## Known Limitations + +- The slice materially strengthened follow-up drafting, but it still does not introduce a separate rich reply-specific workflow beyond thread-aware subject/context reuse; deeper reply-mode specialization can still be expanded later if usage demands it. +- This closeout did not re-run a full live Gmail + browser UAT loop end to end; the trusted evidence for S03 in this worktree is the focused backend/frontend verification plus the production frontend build. +- Follow-up quality still depends on upstream data quality from S01 and S02: weak imported thread metadata or missing saved package material will reduce how specific the draft can be. + +## Follow-ups + +- S04 should consume the new follow-up grounding/status signals to show which jobs are ready for follow-up, missing context, or need attention from overview surfaces. +- S05 should re-run the full live loop with real Gmail continuity plus follow-up drafting to reconfirm milestone trust end to end. + +## Files Created/Modified + +- `JobTrackerApi/Controllers/JobApplicationsController.cs` — assembled correspondence-aware follow-up context, exposed draft grounding metadata, and improved fallback/reply-style subject behavior. +- `JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs` — added focused backend proof that imported thread state plus saved package material change the follow-up draft. +- `JobTrackerApi.Tests/JobTrackerApi.Tests.csproj` — restored missing framework/package references so filtered backend verification commands run directly again. +- `job-tracker-ui/src/components/JobDetailsDialog.tsx` — exposed follow-up grounding in the workspace, kept drafts editable, and clarified the manual-send boundary. +- `job-tracker-ui/src/types.ts` — added the richer `FollowUpDraft` contract used by the workspace. +- `job-tracker-ui/src/job-details-followup-drafts.test.tsx` — proved the Follow-up tab renders context grounding and only sends/logs after explicit user action. + +## Forward Intelligence + +### What the next slice should know +- S03 made the follow-up flow depend on three upstream context sources at once: imported correspondence, saved package fields, and the marker-delimited application-answer draft in `JobApplication.Notes`. If S04 wants actionable table/dashboard indicators, it should reuse the backend grounding signals instead of trying to infer readiness from scattered raw fields in the browser. + +### What's fragile +- `JobApplication.Notes` marker parsing for `<<>> ... <<>>` — if another slice starts appending free-form notes around that block incorrectly, follow-up context quality will silently degrade because saved application-answer reuse depends on parsing that exact marker format. + +### Authoritative diagnostics +- `GET /api/jobapplications/{id}/followup-draft` and `JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs` — the endpoint payload is the single source of truth for what grounding the backend believes it has, and the focused backend test is the fastest trustworthy check when prompt assembly or DTO shape changes. +- `job-tracker-ui/src/job-details-followup-drafts.test.tsx` — this is the most reliable UI-level diagnostic for the manual-send boundary and edited-draft payload because it asserts the exact send/log request body after user edits. + +### What assumptions changed +- “The slice may need an isolated backend harness because the filtered test command is not trustworthy.” — no longer true in this worktree; closeout repaired `JobTrackerApi.Tests.csproj`, so the plan-level filtered backend command now passes directly and should replace the older workaround. diff --git a/.gsd/milestones/M001/slices/S03/S03-UAT.md b/.gsd/milestones/M001/slices/S03/S03-UAT.md new file mode 100644 index 0000000..dd2c8fb --- /dev/null +++ b/.gsd/milestones/M001/slices/S03/S03-UAT.md @@ -0,0 +1,92 @@ +# S03: Reply and follow-up drafting from real thread context — UAT + +**Milestone:** M001 +**Written:** 2026-03-24 + +## UAT Type + +- UAT mode: mixed +- Why this mode is sufficient: S03 changes both backend draft assembly and the per-job Follow-up workspace, so the right acceptance script combines artifact-backed checks (focused backend/frontend tests and build) with a human review of draft specificity, editability, and the manual-send boundary. + +## Preconditions + +- The API and UI for this worktree are running from `/home/pi/development/JobTracker/.gsd/worktrees/M001`. +- A job exists with all of the following: + - imported Gmail correspondence tied to the job + - recruiter email populated on the company/job + - saved tailored CV, cover letter, recruiter message, and/or saved application-answer draft material +- The tester can open that job in the job workspace. +- No autonomous outbound-email automation is enabled anywhere in the environment. + +## Smoke Test + +Open one seeded job, switch to the **Follow-up** tab, and confirm the tab shows a generated draft plus a visible context panel that names the thread/package grounding rather than only a blank email form. + +## Test Cases + +### 1. Generate a thread-grounded follow-up draft + +1. Open a job that already has imported correspondence and saved application package material. +2. Open the **Follow-up** tab. +3. Wait for the draft to load. +4. Review the context panel above/alongside the draft. +5. **Expected:** + - a draft subject and body are generated + - the UI shows why the draft was generated now (for example, waiting-update timing) + - the UI shows thread/package grounding such as thread subject, latest sender, context summary, or context signals + - the draft content feels tied to the actual thread stage and saved package context instead of reading like a generic template + +### 2. Edit the draft before sending and verify manual-send behavior + +1. In the **Follow-up** tab, edit the generated subject and/or body. +2. Confirm the recipient field is editable or clearly visible before any send action. +3. Verify the helper text/button copy makes the manual-send boundary explicit. +4. Click **Send and log email**. +5. **Expected:** + - nothing is sent before the explicit button click + - the edited draft text, not the original generated text, is what gets submitted + - the workflow behaves like a manual send/log action rather than background automation + - there is a clear success state or logged result after submission + +### 3. Confirm the sent follow-up reappears in job history/correspondence + +1. After sending/logging the follow-up, switch to the job’s correspondence/history surface. +2. Refresh the job workspace if needed. +3. Find the newly logged outbound follow-up entry. +4. **Expected:** + - the sent/logged follow-up appears back in the same job timeline/correspondence record + - the entry is attached to the correct job and thread context + - the app reflects history continuity instead of treating the send as an isolated compose event + +## Edge Cases + +### Missing package or thin thread context still preserves user control + +1. Open a job that has weaker context than the happy path (for example, imported correspondence but little/no saved package material, or saved package material but only a thin thread). +2. Generate a follow-up draft. +3. **Expected:** + - the app still returns an editable draft or a clearly explained degraded draft state + - the UI reflects missing/limited grounding rather than pretending the draft is strongly informed + - the manual-send boundary remains unchanged: no autonomous send occurs, and the user still must explicitly send/log + +## Failure Signals + +- The Follow-up tab loads only a blank compose form with no visible context grounding. +- The generated draft ignores obvious thread details (subject, recruiter, recent message content) and reads like a generic reminder. +- Editing the draft does not persist into the send/log request. +- A follow-up appears to be sent or logged without an explicit user action. +- The sent follow-up does not return to the job’s correspondence/history surface. +- The tab crashes, build/test regressions appear, or the follow-up endpoint no longer returns the grounding fields expected by the UI. + +## Not Proven By This UAT + +- This UAT does not prove Gmail OAuth/import itself; that was covered by S01 and is only consumed here as prerequisite context. +- This UAT does not prove end-to-end milestone coherence across table/dashboard/control-loop surfaces; S04 and S05 still own that broader workflow validation. + +## Notes for Tester + +Use a job with real-looking imported thread content and saved package material; otherwise the quality signal will be too weak to judge whether S03 actually improved grounding. If the environment cannot support live send/log execution, fall back to the focused checks that already passed for this slice: + +- `$HOME/.dotnet/dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsFollowUpDraftTests` +- `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 run build` diff --git a/.gsd/milestones/M001/slices/S03/tasks/T01-SUMMARY.md b/.gsd/milestones/M001/slices/S03/tasks/T01-SUMMARY.md index 16512f3..0f38c29 100644 --- a/.gsd/milestones/M001/slices/S03/tasks/T01-SUMMARY.md +++ b/.gsd/milestones/M001/slices/S03/tasks/T01-SUMMARY.md @@ -6,23 +6,40 @@ files: - JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs 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/followuptests/FollowUpTests.csproj...' + - $HOME/.dotnet/dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsFollowUpDraftTests +observability_surfaces: + - GET /api/jobapplications/{id}/followup-draft + - POST /api/jobapplications/{id}/send-followup + - GET /api/correspondence/{jobId} + - JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs --- Strengthened follow-up draft generation so it now consumes imported thread context and saved application package material instead of relying mostly on job summary text. -What changed: +## What changed + - `JobTrackerApi/Controllers/JobApplicationsController.cs` - - expanded `FollowUpDraftDto` to expose context summary, context signals, thread subject, and last-correspondence metadata for the workspace + - expanded `FollowUpDraftDto` to expose `contextSummary`, `contextSignals`, `threadSubject`, and last-correspondence metadata for the workspace - added helpers to parse the saved application-answer draft from notes, derive reply-style subjects from the latest thread, and assemble follow-up context signals from recruiter/package/thread state - enriched `GetFollowUpDraft(...)` so the AI prompt now includes imported correspondence context, recruiter details, saved tailored CV / cover letter / recruiter message / application-answer material, and thread-stage cues - improved the fallback body so it still reflects saved/thread context when AI output is unavailable - `JobTrackerApi.Tests/JobApplicationsFollowUpDraftTests.cs` - added focused backend proof that follow-up draft generation reflects imported thread and saved package state -Verification: -- Backend host build passed with `$HOME/.dotnet/dotnet build JobTrackerApi/JobTrackerApi.csproj` -- Focused follow-up draft backend test passed in an isolated Docker harness (`1 passed`) +## Diagnostics -Important caveat: -- The broader `JobTrackerApi.Tests` project still has unrelated compile drift, so focused follow-up verification was isolated rather than run through the full test project. +- Call `GET /api/jobapplications/{id}/followup-draft` and inspect `contextSummary`, `contextSignals`, `threadSubject`, `lastCorrespondenceFrom`, and `lastCorrespondenceAt` to confirm the draft is grounded in saved package material plus imported correspondence. +- Call `POST /api/jobapplications/{id}/send-followup` only after editing the returned draft; this is the authoritative manual-send/log boundary surface for the slice. +- Use `GET /api/correspondence/{jobId}` after a manual send/log to confirm the outbound follow-up appears back in the job timeline rather than remaining transient UI-only state. +- Re-run `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsFollowUpDraftTests` when follow-up prompt assembly, DTO shape, or fallback behavior changes. + +## Verification Evidence + +| Check | Command | Exit code | Verdict | Duration | +|---|---|---:|---|---| +| Backend build | `$HOME/.dotnet/dotnet build JobTrackerApi/JobTrackerApi.csproj` | 0 | PASS | 00:00:03 | +| Focused backend follow-up test | `$HOME/.dotnet/dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsFollowUpDraftTests` | 0 | PASS | 00:00:01 | + +## Important caveat + +- The original task had to fall back to an isolated harness because `JobTrackerApi.Tests.csproj` was missing required framework/package references. During slice closeout, the test project was repaired so the plan-level filtered command now runs directly in this worktree again. diff --git a/.gsd/milestones/M001/slices/S03/tasks/T02-SUMMARY.md b/.gsd/milestones/M001/slices/S03/tasks/T02-SUMMARY.md index 0d38f23..aa6b7bf 100644 --- a/.gsd/milestones/M001/slices/S03/tasks/T02-SUMMARY.md +++ b/.gsd/milestones/M001/slices/S03/tasks/T02-SUMMARY.md @@ -8,11 +8,17 @@ files: verification: - 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 run build +observability_surfaces: + - job-tracker-ui/src/components/JobDetailsDialog.tsx (Follow-up tab) + - job-tracker-ui/src/job-details-followup-drafts.test.tsx + - GET /api/jobapplications/{id}/followup-draft + - POST /api/jobapplications/{id}/send-followup --- Refined the Follow-up tab so it exposes the thread and saved-package grounding behind the draft instead of behaving like a generic email form. -What changed: +## What changed + - `job-tracker-ui/src/types.ts` - added a typed `FollowUpDraft` contract aligned to the richer backend response - `job-tracker-ui/src/components/JobDetailsDialog.tsx` @@ -23,9 +29,20 @@ What changed: - `job-tracker-ui/src/job-details-followup-drafts.test.tsx` - added focused frontend proof that the Follow-up tab shows thread grounding, keeps sending manual, and posts the edited draft through the send/log endpoint -Verification: -- Focused follow-up workspace test passed: `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-followup-drafts.test.tsx` -- Production frontend build passed: `CI=true npm --prefix job-tracker-ui run build` +## Diagnostics -Runtime note: -- Browser-based local UAT against this worktree was attempted but blocked by environment issues: port 3000 is served by an external nginx bundle unrelated to this worktree, the CRA dev server crashed under Node 25 in `fork-ts-checker`, and the browser harness did not successfully execute the locally served static bundle. Automated verification for the implemented code paths still passed. +- Open the Follow-up tab in `job-tracker-ui/src/components/JobDetailsDialog.tsx` and confirm the context panel renders `threadSubject`, `contextSummary`, and `contextSignals` from the backend instead of showing only a blank compose form. +- Edit the generated body before pressing **Send and log email**; the UI should preserve the edited body and only call `POST /api/jobapplications/{id}/send-followup` after the explicit button click. +- Re-run `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-followup-drafts.test.tsx` after changes to follow-up state wiring, helper text, or send/log payloads. +- Re-run `CI=true npm --prefix job-tracker-ui run build` after UI changes to catch contract drift or production-only compilation regressions. + +## Verification Evidence + +| Check | Command | Exit code | Verdict | Duration | +|---|---|---:|---|---| +| Focused follow-up workspace test | `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-followup-drafts.test.tsx` | 0 | PASS | 00:00:02 | +| Production frontend build | `CI=true npm --prefix job-tracker-ui run build` | 0 | PASS | 00:00:24 | + +## Runtime note + +- Earlier local browser UAT was blocked by an unrelated port-3000 nginx bundle and a CRA dev-server crash under Node 25. The production build and focused follow-up UI test both passed during closeout, so the durable verification path for this task is the filtered React test plus the build output rather than the old dev-server attempt. diff --git a/JobTrackerApi.Tests/JobTrackerApi.Tests.csproj b/JobTrackerApi.Tests/JobTrackerApi.Tests.csproj index e972ef7..5024cf6 100644 --- a/JobTrackerApi.Tests/JobTrackerApi.Tests.csproj +++ b/JobTrackerApi.Tests/JobTrackerApi.Tests.csproj @@ -14,10 +14,14 @@ all + + + +