From b2d9358d5835995de80a3922f9780a36e98f5c95 Mon Sep 17 00:00:00 2001 From: cesnimda Date: Tue, 24 Mar 2026 12:32:21 +0100 Subject: [PATCH] chore(M001/S02): auto-commit after complete-slice --- .gsd/KNOWLEDGE.md | 2 + .gsd/PROJECT.md | 2 +- .gsd/REQUIREMENTS.md | 6 +- .gsd/journal/2026-03-24.jsonl | 5 + .gsd/milestones/M001/M001-ROADMAP.md | 2 +- .../milestones/M001/slices/S02/S02-SUMMARY.md | 123 ++++++++++++++---- .gsd/milestones/M001/slices/S02/S02-UAT.md | 91 +++++++++++++ .../M001/slices/S02/tasks/T01-SUMMARY.md | 25 +++- .../M001/slices/S02/tasks/T02-SUMMARY.md | 37 ++++-- 9 files changed, 242 insertions(+), 51 deletions(-) create mode 100644 .gsd/milestones/M001/slices/S02/S02-UAT.md diff --git a/.gsd/KNOWLEDGE.md b/.gsd/KNOWLEDGE.md index 2009c01..4397ec3 100644 --- a/.gsd/KNOWLEDGE.md +++ b/.gsd/KNOWLEDGE.md @@ -2,3 +2,5 @@ - `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter GmailControllerTests` still compiles the entire `JobTrackerApi.Tests` project before filtering execution. If unrelated controller tests drift from production signatures, the Gmail slice verification command will fail at compile time even when `GmailControllerTests` itself is correct. - 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. diff --git a/.gsd/PROJECT.md b/.gsd/PROJECT.md index c33746d..81ef4c4 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 now 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 tightened the package loop around that context: backend package generation now 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 turned follow-up drafting into a real thread-aware workspace flow, and S04 connected the dashboard, reminders, and table into one routed daily control loop that opens the same per-job workspace state. The next phase is milestone revalidation plus end-to-end trust/polish on top of the now-live Gmail continuity loop. +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. ## Architecture / Key Patterns diff --git a/.gsd/REQUIREMENTS.md b/.gsd/REQUIREMENTS.md index 32f914c..683dcd1 100644 --- a/.gsd/REQUIREMENTS.md +++ b/.gsd/REQUIREMENTS.md @@ -69,8 +69,8 @@ This file is the explicit capability and coverage contract for the project. - Source: user - Primary owning slice: M001/S02 - Supporting slices: M001/S05 -- Validation: Validated by M001/S02: backend application-package generation now uses recruiter/job/profile/attachment/imported-correspondence context, focused backend tests pass in an isolated harness, and the Tailored CV workspace test proves generation, editing, save, and saved-state redisplay behavior. -- Notes: S02 proved the application-package drafts are materially more specific and persist as job-tied working material. Broader end-to-end live-loop revalidation still remains in M001/S05. +- Validation: Validated by M001/S02: `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsApplicationPackageTests` now passes with package generation using recruiter/job/profile/attachment/imported-correspondence context, and `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-generated-drafts.test.tsx` proves generation, editing, save, and saved-state redisplay behavior in the job workspace. +- Notes: S02 also proved the saved application-answer loop by replacing the marker-delimited notes block instead of appending indefinitely. ### R004 — The app must generate follow-up and reply drafts from the imported job, saved application material, and correspondence context tied to that job. - Class: primary-user-loop @@ -203,7 +203,7 @@ This file is the explicit capability and coverage contract for the project. |---|---|---|---|---|---| | R001 | primary-user-loop | validated | M001/S01 | M001/S05 | S01 completed with a job-scoped Gmail import loop wired into the job workspace: backend `GET /api/gmail/job-candidates` uses the owned job as context, imports target that job directly, and focused UI verification passed in `job-tracker-ui/src/correspondence-gmail-import.test.tsx`. | | R002 | integration | validated | M001/S01 | M001/S03, M001/S05 | Validated by M001/S01: backend `POST /api/gmail/refresh-linked-threads` now refreshes already-linked Gmail thread ids for one owned job, imports only unseen replies into the same job with duplicate-safe counts, focused `GmailControllerTests` pass via `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter GmailControllerTests`, and `job-tracker-ui/src/correspondence-gmail-import.test.tsx` proves the workspace auto-refresh path shows a later Gmail reply without manual re-import. | -| R003 | differentiator | validated | M001/S02 | M001/S05 | Validated by M001/S02: backend application-package generation now uses recruiter/job/profile/attachment/imported-correspondence context, focused backend tests pass in an isolated harness, and the Tailored CV workspace test proves generation, editing, save, and saved-state redisplay behavior. | +| R003 | differentiator | validated | M001/S02 | M001/S05 | Validated by M001/S02: `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsApplicationPackageTests` now passes with package generation using recruiter/job/profile/attachment/imported-correspondence context, and `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-generated-drafts.test.tsx` proves generation, editing, save, and saved-state redisplay behavior in the job workspace. | | R004 | primary-user-loop | validated | M001/S03 | M001/S01, M001/S02, M001/S05 | Validated by M001/S03: follow-up draft generation now consumes imported correspondence plus saved application package material, focused backend follow-up tests pass in an isolated harness, the focused React follow-up test passes, and browser verification on the built branch UI proved the grounded follow-up draft plus manual send/log flow back into correspondence. | | 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. | diff --git a/.gsd/journal/2026-03-24.jsonl b/.gsd/journal/2026-03-24.jsonl index 31216db..15a44e6 100644 --- a/.gsd/journal/2026-03-24.jsonl +++ b/.gsd/journal/2026-03-24.jsonl @@ -33,3 +33,8 @@ {"ts":"2026-03-24T11:14:31.524Z","flowId":"76338692-ac8b-4668-98c6-5062fdd544ff","seq":2,"eventType":"dispatch-match","rule":"summarizing → complete-slice","data":{"unitType":"complete-slice","unitId":"M001/S01"}} {"ts":"2026-03-24T11:14:31.532Z","flowId":"76338692-ac8b-4668-98c6-5062fdd544ff","seq":3,"eventType":"unit-start","data":{"unitType":"complete-slice","unitId":"M001/S01"}} {"ts":"2026-03-24T11:27:04.508Z","flowId":"76338692-ac8b-4668-98c6-5062fdd544ff","seq":4,"eventType":"unit-end","data":{"unitType":"complete-slice","unitId":"M001/S01","status":"completed","artifactVerified":true},"causedBy":{"flowId":"76338692-ac8b-4668-98c6-5062fdd544ff","seq":3}} +{"ts":"2026-03-24T11:27:04.912Z","flowId":"76338692-ac8b-4668-98c6-5062fdd544ff","seq":5,"eventType":"iteration-end","data":{"iteration":4}} +{"ts":"2026-03-24T11:27:04.912Z","flowId":"1e956ae8-b804-4454-8fba-0d7983c8f804","seq":1,"eventType":"iteration-start","data":{"iteration":5}} +{"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}} diff --git a/.gsd/milestones/M001/M001-ROADMAP.md b/.gsd/milestones/M001/M001-ROADMAP.md index d26cc1e..d72d0dd 100644 --- a/.gsd/milestones/M001/M001-ROADMAP.md +++ b/.gsd/milestones/M001/M001-ROADMAP.md @@ -55,7 +55,7 @@ This milestone is complete only when all are true: - [x] **S01: Smarter Gmail import and matching** `risk:high` `depends:[]` > After this: User can connect Gmail, review likely messages or threads for a job, import a message or full thread, and trust linked Gmail threads to stay current on that job without manual re-import. -- [ ] **S02: Stronger AI application package drafting** `risk:high` `depends:[S01]` +- [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]` diff --git a/.gsd/milestones/M001/slices/S02/S02-SUMMARY.md b/.gsd/milestones/M001/slices/S02/S02-SUMMARY.md index ea901ec..687bc44 100644 --- a/.gsd/milestones/M001/slices/S02/S02-SUMMARY.md +++ b/.gsd/milestones/M001/slices/S02/S02-SUMMARY.md @@ -1,34 +1,101 @@ --- -title: S02 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/apptests/AppPkgTests.csproj...' - - CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-generated-drafts.test.tsx +id: S02 +parent: M001 +milestone: M001 +provides: + - stronger application-package generation that uses imported correspondence, recruiter/job context, profile CV structure, and attachment signals + - a persisted package workspace inside the job dialog for tailored CV, cover letter, recruiter message, and application-answer draft material +requires: + - slice: S01 + provides: imported and auto-refreshed job-linked correspondence plus trusted thread metadata for package context assembly +affects: + - S03 +key_files: + - JobTrackerApi/Controllers/JobApplicationsController.cs + - JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs + - job-tracker-ui/src/components/JobDetailsDialog.tsx + - job-tracker-ui/src/job-details-generated-drafts.test.tsx +key_decisions: + - D006: persist the application-answer draft as a replaceable marker-delimited notes block until a dedicated field exists +patterns_established: + - assemble AI package context from persisted job, recruiter, correspondence, saved-draft, profile-CV, and attachment signals before prompting + - treat generated artifacts as editable workspace state with explicit saved/generated/unsaved status rather than disposable preview output +observability_surfaces: + - POST /api/jobapplications/{id}/generate-application-package + - PUT /api/jobapplications/{id}/tailored-cv + - PUT /api/jobapplications/{id}/application-drafts + - job-tracker-ui/src/job-details-generated-drafts.test.tsx + - JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs +duration: 2 tasks +verification_result: passed +completed_at: 2026-03-24 --- -S02 made the application-package flow materially more useful and more trustworthy. +# S02: Stronger AI application package drafting -Delivered: -- stronger backend package-context assembly in `JobTrackerApi/Controllers/JobApplicationsController.cs` - - imported correspondence is now part of package generation - - recruiter/job/profile/attachment context is threaded into the package prompts deliberately - - returned key points can include correspondence-derived and attachment-derived signals -- focused backend proof in `JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs` - - package generation responds to imported correspondence and recruiter context - - notes-saving behavior for application-answer material replaces rather than appends endlessly -- stronger job workspace loop in `job-tracker-ui/src/components/JobDetailsDialog.tsx` - - saved job material loads back into the Tailored CV tab as the starting workspace state - - generation replaces the working draft for tailored CV, cover letter, application answer, and recruiter message - - save writes the package back as job-tied working material rather than leaving it as a temporary preview - - the UI distinguishes saved state, generated-only state, and unsaved edits -- focused frontend proof in `job-tracker-ui/src/job-details-generated-drafts.test.tsx` - - verifies saved-state load, generation, editing, coherent save payload, and saved-state redisplay behavior +**Imported Gmail/job context now feeds a persisted application-package workspace that generates, edits, saves, and reloads job-specific draft material instead of one-shot generic previews.** -Net effect: -- S01 imported Gmail context now materially improves draft quality instead of sitting unused. -- The job workspace behaves more like a preparation surface the user can trust and reuse later. -- The manual-send boundary remains intact; S02 improved drafting and persistence only. +## What Happened -Remaining milestone gap after S02: -- S03 still needs to turn the imported thread context plus saved package state into strong reply/follow-up drafting. +S02 closed the main draft-quality gap by wiring S01’s imported correspondence into application-package generation and then making the Tailored CV tab behave like a real working surface. + +On the backend, `JobTrackerApi/Controllers/JobApplicationsController.cs` now builds package context from more than the job description. Generation explicitly pulls in recruiter identity, job URL, imported correspondence, saved package material already tied to the job, profile CV structure, and selected attachment signals. That context is reused across tailored CV, cover letter, application answer, recruiter message, and package key points so the returned artifacts can react to real recruiter/thread context instead of defaulting to generic role-summary language. + +On the frontend, `job-tracker-ui/src/components/JobDetailsDialog.tsx` now treats tailored CV, cover letter, recruiter message, and application-answer text as one package workspace. Reopening the job loads the saved copy back into the editors, generation replaces the current working copy for all package artifacts, save persists the package back to the job, and reset restores the last saved state. Status chips make the persistence state legible by distinguishing `Saved to job`, `Generated only`, and `Unsaved edits`. + +S02 also established a temporary but stable persistence contract for the application-answer draft: until a dedicated field exists, it lives in a marker-delimited block inside `JobApplication.Notes`, and save replaces that block rather than appending indefinitely. That keeps the workspace trustworthy and gives S03 a reliable place to read package context back from. + +The net effect is that S01 correspondence is no longer just imported and displayed; it now materially influences package drafting, and the resulting artifacts persist as reusable job workspace material. + +## Verification + +- `$HOME/.dotnet/dotnet build JobTrackerApi/JobTrackerApi.csproj` — passed +- `$HOME/.dotnet/dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsApplicationPackageTests` — passed (2 tests) +- `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-generated-drafts.test.tsx` — passed (2 tests) +- Observability surfaces confirmed by implementation/tests: + - `POST /api/jobapplications/{id}/generate-application-package` now emits package artifacts grounded in correspondence/recruiter/job context + - `PUT /api/jobapplications/{id}/tailored-cv` and `PUT /api/jobapplications/{id}/application-drafts` form the durable save loop the workspace depends on + - focused backend/frontend tests cover package generation specificity, notes replacement, saved-state load, edit, save, and redisplay behavior + +## New Requirements Surfaced + +- none + +## Deviations + +- The plan did not call out storage for the application-answer draft, but execution required a concrete persistence strategy before the workspace could be trustworthy. S02 therefore adopted the marker-delimited notes-block approach captured in D006 instead of introducing a new schema field mid-slice. + +## Known Limitations + +- The application-answer draft is still stored inside `JobApplication.Notes` rather than a first-class field, so downstream work must keep honoring the marker-block contract. +- Automated verification proves context wiring and persistence loops, but it does not replace human judgment on whether the generated writing feels genuinely strong enough for a real application; that still needs live UAT with real imported correspondence and AI output. +- Draft quality still depends on the quality of the imported correspondence, recruiter metadata, profile CV structure, and selected attachments available on the job. + +## Follow-ups + +- S03 should consume the saved package workspace artifacts, including the marker-delimited application-answer block, instead of reconstructing package context from scratch. +- A later milestone can promote the application-answer draft to a dedicated persisted field if the notes-block workaround starts constraining editing, analytics, or downstream composition. +- Milestone-level live UAT should include at least one job with strong imported Gmail context to judge whether the upgraded generator actually feels specific enough to start from. + +## Files Created/Modified + +- `JobTrackerApi/Controllers/JobApplicationsController.cs` — strengthened package-context assembly, notes replacement behavior, and generation/save contracts +- `JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs` — focused backend proof for correspondence-aware package output and notes replacement +- `job-tracker-ui/src/components/JobDetailsDialog.tsx` — converted the Tailored CV tab into a coherent generate/edit/save/reset package workspace +- `job-tracker-ui/src/job-details-generated-drafts.test.tsx` — verified saved-state load, generation, editing, coherent save payload, and redisplay behavior +- `.gsd/REQUIREMENTS.md` — refreshed R003 validation to reflect the direct filtered backend test plus frontend workspace proof +- `.gsd/KNOWLEDGE.md` — recorded the marker-delimited application-answer persistence contract and the authoritative direct S02 test command + +## Forward Intelligence + +### What the next slice should know +- S03 can now assume package context lives in durable job fields: `tailoredCvText`, `coverLetterText`, `recruiterMessageDraft`, and the marker-delimited application-answer block inside `notes`; use those saved values as the baseline context for reply/follow-up generation. + +### What's fragile +- Application-answer persistence via the `<<>> ... <<>>` notes block — downstream code must replace/parse that block consistently or the workspace will drift back into duplicate or stale-answer behavior. + +### Authoritative diagnostics +- `JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs` and `job-tracker-ui/src/job-details-generated-drafts.test.tsx` — these are the tightest trustworthy checks for whether package generation is context-aware and whether the workspace save/reload loop still works end to end. + +### What assumptions changed +- Earlier task execution assumed the filtered backend verification might still need an isolated harness because of broader test-project drift — in this worktree, `dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsApplicationPackageTests` now passes directly and should be treated as the primary S02 regression check. diff --git a/.gsd/milestones/M001/slices/S02/S02-UAT.md b/.gsd/milestones/M001/slices/S02/S02-UAT.md new file mode 100644 index 0000000..1012fb6 --- /dev/null +++ b/.gsd/milestones/M001/slices/S02/S02-UAT.md @@ -0,0 +1,91 @@ +# S02: Stronger AI application package drafting — UAT + +**Milestone:** M001 +**Written:** 2026-03-24 + +## UAT Type + +- UAT mode: mixed +- Why this mode is sufficient: this slice needs both artifact proof (generation/save contracts and persisted reload behavior) and human judgment that the drafts actually feel specific enough to start from. + +## Preconditions + +- The app frontend and API are running against this worktree. +- The tester can sign in as a user with a parsed profile CV already available. +- At least one job exists with: + - company + recruiter information + - imported Gmail correspondence from S01 linked to the job + - optional but preferred AI-selected attachments for extra context +- The local AI summarizer/service used by `generate-application-package` is configured and reachable. +- The tester knows which job has the strongest imported recruiter/thread context so draft specificity is easy to judge. + +## Smoke Test + +Open a job with imported correspondence, switch to the Tailored CV tab, click **Generate package**, and confirm all four editable artifacts populate without errors: tailored CV, cover letter, recruiter message, and application answer. + +## Test Cases + +### 1. Generate a package that reflects imported job and correspondence context + +1. Open a job that already has imported Gmail correspondence in the job workspace. +2. In the Tailored CV tab, confirm at least one attachment is selected for AI context if relevant. +3. Click **Generate package**. +4. Wait for generation to finish. +5. Review the tailored CV, cover letter, recruiter message, and application answer together. +6. **Expected:** the generated package mentions concrete job/company/recruiter details and reflects correspondence-specific context or tone rather than reading like a generic template. + +### 2. Save edited package material as durable job workspace output + +1. Starting from a generated package, edit all or some of these fields: tailored CV, cover letter, recruiter message, application answer. +2. Confirm the status chips change to show **Unsaved edits** for the edited artifacts. +3. Click **Save** for the package workspace. +4. Wait for the save to complete. +5. **Expected:** save succeeds without duplication, the edited values remain visible, and the status chips move to **Saved to job**. + +### 3. Reopen the job and verify the last saved package reloads + +1. Close the job details dialog after saving. +2. Reopen the same job. +3. Return to the Tailored CV tab. +4. **Expected:** the saved tailored CV, cover letter, recruiter message, and application answer reload as the current workspace state; nothing falls back to blank or a previous generated-only draft. + +### 4. Regenerate after saving and use reset-to-saved safely + +1. With a saved package already present, click **Generate package** again. +2. Confirm the editors are replaced with the new generated working copy. +3. Make one more manual edit so the status changes to **Unsaved edits**. +4. Click **Reset to saved**. +5. **Expected:** the unsaved regeneration/edit is discarded and the workspace returns exactly to the last saved job-tied material. + +## Edge Cases + +### Saved application answer does not duplicate on repeated saves + +1. Save a package with a distinct application-answer draft. +2. Edit only the application answer and save again. +3. Close and reopen the job. +4. **Expected:** only the latest application-answer draft is present; earlier answers are replaced, not appended repeatedly into the notes-backed storage. + +### Empty saved package still distinguishes generated-only state + +1. Open a job with no previously saved package material. +2. Generate the package but do not click Save. +3. **Expected:** generated text appears, but the relevant status chips show **Generated only** rather than **Saved to job**. + +## Failure Signals + +- Generate package returns an error or leaves one or more of the four artifacts empty despite available context. +- Drafts ignore obvious recruiter/company/correspondence details and read like generic boilerplate. +- Saving succeeds visually but reopening the job loses edits or restores stale values. +- The application answer repeats previous saved copies instead of replacing the prior draft. +- Status chips do not match reality, for example showing **Saved to job** before any save or failing to show **Unsaved edits** after changes. + +## Not Proven By This UAT + +- This UAT does not prove Gmail import or linked-thread refresh; that belongs to S01. +- This UAT does not prove reply/follow-up draft generation from saved package context; that belongs to S03. +- This UAT does not prove final end-to-end milestone quality across dashboard/table/job-loop navigation; later slices and final milestone UAT must cover that. + +## Notes for Tester + +Use the job with the richest imported recruiter/thread context first; this slice is about whether that context materially improves draft usefulness. If draft quality feels only marginally better, note which missing signals (recruiter identity, thread details, attachment evidence, profile CV structure) seem absent so S03/S05 can inspect the package-context assembly path. diff --git a/.gsd/milestones/M001/slices/S02/tasks/T01-SUMMARY.md b/.gsd/milestones/M001/slices/S02/tasks/T01-SUMMARY.md index 6483823..97ebffc 100644 --- a/.gsd/milestones/M001/slices/S02/tasks/T01-SUMMARY.md +++ b/.gsd/milestones/M001/slices/S02/tasks/T01-SUMMARY.md @@ -4,14 +4,19 @@ status: done files: - JobTrackerApi/Controllers/JobApplicationsController.cs - JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs +observability_surfaces: + - POST /api/jobapplications/{id}/generate-application-package + - JobApplicationsApplicationPackageTests in JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs + - persisted package fields and notes markers on JobApplication records after generation/save loops 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/apptests/AppPkgTests.csproj...' + - $HOME/.dotnet/dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsApplicationPackageTests --- Strengthened application-package generation so it now consumes imported correspondence and recruiter/job context instead of relying mostly on job description text plus the profile CV. -What changed: +## What changed + - `JobTrackerApi/Controllers/JobApplicationsController.cs` - added `BuildCorrespondenceContextAsync(...)` to gather recent imported correspondence, participants, thread ids, and AI-derived package signals - enriched `generate-application-package` context with: @@ -24,9 +29,15 @@ What changed: - `JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs` - added focused backend proof that package generation reacts to imported correspondence and recruiter context rather than falling back to generic output -Verification: -- Native backend build passed with `$HOME/.dotnet/dotnet build JobTrackerApi/JobTrackerApi.csproj` -- Focused application-package backend test passed in an isolated Docker harness (`1 passed`) +## Diagnostics -Important caveat: -- As with S01 backend verification, the repository’s broader `JobTrackerApi.Tests` project still has unrelated compile drift, so the focused package test was isolated instead of relying on the full test project. +- Call `POST /api/jobapplications/{id}/generate-application-package` on a job that already has imported correspondence; inspect whether the response package now includes recruiter-aware wording, correspondence-derived `keyPoints`, and non-generic draft content. +- Check `JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs` first when the package feels generic; it is the narrowest automated proof that imported correspondence and recruiter context reach the generator. +- If runtime output looks weak, inspect the job record and linked `Correspondence` rows first; this generator now depends on persisted correspondence quality rather than only the raw job description. + +## Verification Evidence + +| Command | Exit code | Verdict | Duration | +|---|---:|---|---| +| `$HOME/.dotnet/dotnet build JobTrackerApi/JobTrackerApi.csproj` | 0 | PASS | 4.8s | +| `$HOME/.dotnet/dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsApplicationPackageTests` | 0 | PASS | 8.4s | diff --git a/.gsd/milestones/M001/slices/S02/tasks/T02-SUMMARY.md b/.gsd/milestones/M001/slices/S02/tasks/T02-SUMMARY.md index 529a473..b2b30e4 100644 --- a/.gsd/milestones/M001/slices/S02/tasks/T02-SUMMARY.md +++ b/.gsd/milestones/M001/slices/S02/tasks/T02-SUMMARY.md @@ -4,15 +4,23 @@ status: done files: - job-tracker-ui/src/components/JobDetailsDialog.tsx - job-tracker-ui/src/job-details-generated-drafts.test.tsx + - JobTrackerApi/Controllers/JobApplicationsController.cs + - JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs +observability_surfaces: + - Tailored CV tab in job-tracker-ui/src/components/JobDetailsDialog.tsx + - PUT /api/jobapplications/{id}/tailored-cv + - PUT /api/jobapplications/{id}/application-drafts + - job-tracker-ui/src/job-details-generated-drafts.test.tsx verification: - CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-generated-drafts.test.tsx - $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/apptests/AppPkgTests.csproj...' + - $HOME/.dotnet/dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsApplicationPackageTests --- Turned the Tailored CV tab into a real package workspace instead of a one-shot draft preview. -What changed: +## What changed + - `job-tracker-ui/src/components/JobDetailsDialog.tsx` - added package workspace state for cover letter, application answer, and recruiter message alongside the tailored CV - initialized that workspace from saved job material so reopening the dialog shows the last trusted copy, not just blank/generated state @@ -23,14 +31,21 @@ What changed: - normalized application-answer persistence into a replaceable notes block instead of endlessly appending - `job-tracker-ui/src/job-details-generated-drafts.test.tsx` - expanded the focused dialog test to prove saved material loads into the workspace, generation replaces the working copy, edits can be saved, and the save payload reflects the coherent package state +- backend support tightened during T02 + - `JobTrackerApi/Controllers/JobApplicationsController.cs`: `SaveApplicationDrafts` now replaces `Notes` when notes are provided, which makes the saved application-answer loop trustworthy instead of append-only + - `JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs`: added focused proof that notes replacement no longer appends indefinitely -Backend support tightened during T02: -- `JobTrackerApi/Controllers/JobApplicationsController.cs` - - `SaveApplicationDrafts` now replaces `Notes` when notes are provided, which makes the saved application-answer loop trustworthy instead of append-only -- `JobTrackerApi.Tests/JobApplicationsApplicationPackageTests.cs` - - added focused proof that notes replacement no longer appends indefinitely +## Diagnostics -Verification: -- Frontend focused test passed: `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-generated-drafts.test.tsx` -- Backend host build passed: `$HOME/.dotnet/dotnet build JobTrackerApi/JobTrackerApi.csproj` -- Focused backend package tests passed in isolated Docker harness (`2 passed`) +- Open the Tailored CV tab and inspect the four package editors plus status chips; `Saved to job`, `Generated only`, and `Unsaved edits` are the fastest UI-level signals for whether persistence is working. +- Inspect the payloads to `PUT /api/jobapplications/{id}/tailored-cv` and `PUT /api/jobapplications/{id}/application-drafts` when save behavior looks wrong; the package loop depends on both requests succeeding with aligned content. +- Check `job-tracker-ui/src/job-details-generated-drafts.test.tsx` first for regressions in regenerate/edit/save/reset behavior; it is the narrowest proof of the workspace contract. +- If the application answer keeps duplicating, inspect the marker-delimited block inside `JobApplication.Notes`; the persistence contract now replaces the `<<>> ... <<>>` block instead of appending. + +## Verification Evidence + +| Command | Exit code | Verdict | Duration | +|---|---:|---|---| +| `CI=true npm --prefix job-tracker-ui test -- --watch=false --runTestsByPath src/job-details-generated-drafts.test.tsx` | 0 | PASS | 8.3s | +| `$HOME/.dotnet/dotnet build JobTrackerApi/JobTrackerApi.csproj` | 0 | PASS | 4.8s | +| `$HOME/.dotnet/dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsApplicationPackageTests` | 0 | PASS | 8.4s |