chore(M001/S05): auto-commit after research-slice

This commit is contained in:
2026-03-24 14:00:19 +01:00
parent f3398c5ca6
commit b3651e7441
4 changed files with 143 additions and 16 deletions
+4
View File
@@ -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}}
@@ -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
}
]
}
@@ -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 `<<<APPLICATION_ANSWER_DRAFT>>>` marker block; any S05 readiness logic must not treat all notes as generic free text.
## Common Pitfalls
- **Treating `notes` as generic readiness state** — in this milestone, `notes` may contain the persisted application-answer draft block. If S05 wants a better package/trust signal, do not keep leaning on `!job.notes` alone.
- **Duplicating action inference in multiple 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 |