From b3651e7441e50eb1e6e5a19bc94937ffb9d4f53c Mon Sep 17 00:00:00 2001 From: cesnimda Date: Tue, 24 Mar 2026 14:00:19 +0100 Subject: [PATCH] chore(M001/S05): auto-commit after research-slice --- .bg-shell/manifest.json | 17 +-- .gsd/journal/2026-03-24.jsonl | 4 + .../M001/slices/S04/tasks/T02-VERIFY.json | 19 +++ .../M001/slices/S05/S05-RESEARCH.md | 119 ++++++++++++++++++ 4 files changed, 143 insertions(+), 16 deletions(-) create mode 100644 .gsd/milestones/M001/slices/S04/tasks/T02-VERIFY.json create mode 100644 .gsd/milestones/M001/slices/S05/S05-RESEARCH.md diff --git a/.bg-shell/manifest.json b/.bg-shell/manifest.json index 6ea178e..0637a08 100644 --- a/.bg-shell/manifest.json +++ b/.bg-shell/manifest.json @@ -1,16 +1 @@ -[ - { - "id": "dec06736", - "label": "job-tracker-ui-build-serve", - "command": "npx serve -s job-tracker-ui/build -l 4173", - "cwd": "/home/pi/.gsd/projects/a40e97ae9e8f/worktrees/M001", - "ownerSessionFile": "/home/pi/.gsd/sessions/--home-pi-development-JobTracker--/2026-03-24T12-44-27-361Z_3ca4ee97-fc80-4cec-b8e7-f5b393c64480.jsonl", - "persistAcrossSessions": false, - "startedAt": 1774356678346, - "processType": "server", - "group": null, - "readyPattern": "Accepting connections|Local:|http://localhost:4173", - "readyPort": 4173, - "pid": 1141701 - } -] \ No newline at end of file +[] \ No newline at end of file diff --git a/.gsd/journal/2026-03-24.jsonl b/.gsd/journal/2026-03-24.jsonl index f8a0a3b..637d80c 100644 --- a/.gsd/journal/2026-03-24.jsonl +++ b/.gsd/journal/2026-03-24.jsonl @@ -60,3 +60,7 @@ {"ts":"2026-03-24T12:44:27.343Z","flowId":"bc366480-21fd-4628-a971-365a5dbeff56","seq":2,"eventType":"dispatch-match","rule":"executing → execute-task","data":{"unitType":"execute-task","unitId":"M001/S04/T02"}} {"ts":"2026-03-24T12:44:27.348Z","flowId":"bc366480-21fd-4628-a971-365a5dbeff56","seq":3,"eventType":"unit-start","data":{"unitType":"execute-task","unitId":"M001/S04/T02"}} {"ts":"2026-03-24T12:56:42.382Z","flowId":"bc366480-21fd-4628-a971-365a5dbeff56","seq":4,"eventType":"unit-end","data":{"unitType":"execute-task","unitId":"M001/S04/T02","status":"completed","artifactVerified":true},"causedBy":{"flowId":"bc366480-21fd-4628-a971-365a5dbeff56","seq":3}} +{"ts":"2026-03-24T12:56:42.866Z","flowId":"374e2aaa-39f6-4f8b-b7e5-d107156397eb","seq":1,"eventType":"iteration-start","data":{"iteration":2}} +{"ts":"2026-03-24T12:56:42.956Z","flowId":"374e2aaa-39f6-4f8b-b7e5-d107156397eb","seq":2,"eventType":"dispatch-match","rule":"planning (no research, not S01) → research-slice","data":{"unitType":"research-slice","unitId":"M001/S05"}} +{"ts":"2026-03-24T12:56:42.962Z","flowId":"374e2aaa-39f6-4f8b-b7e5-d107156397eb","seq":3,"eventType":"unit-start","data":{"unitType":"research-slice","unitId":"M001/S05"}} +{"ts":"2026-03-24T13:00:19.768Z","flowId":"374e2aaa-39f6-4f8b-b7e5-d107156397eb","seq":4,"eventType":"unit-end","data":{"unitType":"research-slice","unitId":"M001/S05","status":"completed","artifactVerified":true},"causedBy":{"flowId":"374e2aaa-39f6-4f8b-b7e5-d107156397eb","seq":3}} diff --git a/.gsd/milestones/M001/slices/S04/tasks/T02-VERIFY.json b/.gsd/milestones/M001/slices/S04/tasks/T02-VERIFY.json new file mode 100644 index 0000000..0a23add --- /dev/null +++ b/.gsd/milestones/M001/slices/S04/tasks/T02-VERIFY.json @@ -0,0 +1,19 @@ +{ + "schemaVersion": 1, + "taskId": "T02", + "unitId": "M001/S04/T02", + "timestamp": 1774357002857, + "passed": false, + "discoverySource": "none", + "checks": [], + "retryAttempt": 1, + "maxRetries": 2, + "runtimeErrors": [ + { + "source": "bg-shell", + "severity": "crash", + "message": "[jobtracker-api] exitCode=127", + "blocking": true + } + ] +} diff --git a/.gsd/milestones/M001/slices/S05/S05-RESEARCH.md b/.gsd/milestones/M001/slices/S05/S05-RESEARCH.md new file mode 100644 index 0000000..082498d --- /dev/null +++ b/.gsd/milestones/M001/slices/S05/S05-RESEARCH.md @@ -0,0 +1,119 @@ +# 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 |