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

This commit is contained in:
2026-03-24 14:42:09 +01:00
parent c0e2c636df
commit aa325ac7ba
9 changed files with 385 additions and 80 deletions
+1 -1
View File
@@ -64,7 +64,7 @@ This milestone is complete only when all are true:
- [x] **S04: Daily control loop surfaces** `risk:medium` `depends:[S01,S03]`
> After this: The job table works as the primary overview and the follow-up/dashboard surfaces clearly show what needs attention next for an individual user.
- [ ] **S05: End-to-end trust and workflow polish** `risk:low` `depends:[S01,S02,S03,S04]`
- [x] **S05: End-to-end trust and workflow polish** `risk:low` `depends:[S01,S02,S03,S04]`
> After this: The full loop works cleanly in a real environment: import job → generate package → apply externally → import/update correspondence automatically from linked Gmail threads → draft follow-up/reply → track progress confidently.
## Boundary Map
@@ -0,0 +1,118 @@
# S05 Summary — End-to-end trust and workflow polish
## Slice Outcome
S05 completed the final trust-loop assembly for M001. The slice did not add a second workflow; it tightened the existing one so `/jobs`, `/dashboard`, `/reminders`, and the job workspace now describe the same next action, reuse the same saved package state, expose Gmail linked-thread continuity clearly, and keep follow-up drafting separate from any outbound send action.
In practice, this slice turned the milestone from a set of individually working subsystems into one coherent daily-use loop:
- overview surfaces route into the same workspace semantics
- saved package material is treated as explicit reusable workflow state
- linked Gmail thread refresh is visible in the workspace instead of hidden behind import-only UI
- grounded follow-up drafting remains available without crossing the manual-send boundary
## What This Slice Actually Delivered
### 1. Shared workflow trust/action model
S05 centralized workflow trust signals across backend DTOs and frontend routing helpers so overview surfaces no longer guess from free-form `followUpReason` text or raw `notes` presence.
Delivered pattern:
- backend reminders/readiness return normalized `workflowSignal` metadata
- frontend consumes that contract through `job-tracker-ui/src/jobWorkflowSignals.ts`
- `JobTable`, `DashboardView`, and `RemindersView` route from the same source of truth into the existing `/jobs?open=...&tab=...` workspace entry model
This is the main coherence pattern future slices should preserve: if a new daily-loop surface needs a next action, it should consume `workflowSignal`, not invent another heuristic.
### 2. Explicit saved-package trust in the workspace
The Tailored CV workspace now makes the saved-package chain obvious:
- tailored CV, cover letter, application answer, and recruiter message each show save state
- the UI explicitly says saved package material feeds follow-up drafting
- the saved working-material panel shows what later workflow steps can trust and reuse
This matters because S02 already established package persistence, but S05 made that persistence legible as workflow state rather than hidden implementation detail.
### 3. Visible Gmail continuity state in the correspondence workspace
S05 surfaced linked-thread continuity directly in `Correspondence.tsx`.
The workspace now shows:
- Gmail connection state
- linked-thread count
- explicit linked-thread refresh action
- last refresh outcome
That makes the S01 continuity work inspectable in the same workspace where the user reviews correspondence, rather than requiring them to infer freshness from the Gmail import modal alone.
### 4. Integrated trust-loop regression
S05 added `job-tracker-ui/src/end-to-end-trust-loop.test.tsx` as the integrated proof for the slice. The test starts from an overview entry point and verifies the assembled path in one place:
1. open the job from an overview action
2. confirm saved package material is already present
3. confirm linked-thread refresh updates correspondence without re-importing the thread
4. confirm the follow-up draft is grounded in saved package + correspondence context
5. confirm drafting/regeneration does not trigger send behavior
This is now the best single regression to read when future work risks breaking the milestones core loop.
## Patterns Established
- **Workflow actions come from normalized workflow signals.** Do not parse `followUpReason` strings or generic `notes` content in overview surfaces.
- **Saved package state is explicit workflow state.** Future slices should continue treating tailored CV / cover letter / application answer / recruiter message as reusable job-scoped material.
- **Continuity status belongs in the main workspace.** If refresh or sync trust matters, expose its state where the user does the work.
- **Integrated proof should start from an overview surface.** Final-loop regressions should validate real entry routing, not isolated component state only.
- **Manual-send boundary must stay explicit.** Draft generation/regeneration and outbound send must remain visibly separate.
## Verification Run
All slice-plan command checks passed in this worktree.
### Backend
- `~/.gsd/agent/bin/dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsWorkflowSignalsTests`
### Frontend tests
- `cd job-tracker-ui && CI=true ./node_modules/.bin/react-scripts test --watch=false --runTestsByPath src/workflow-trust-signals.test.tsx`
- `cd job-tracker-ui && CI=true ./node_modules/.bin/react-scripts test --watch=false --runTestsByPath src/end-to-end-trust-loop.test.tsx`
- `cd job-tracker-ui && CI=true ./node_modules/.bin/react-scripts test --watch=false --runTestsByPath src/correspondence-gmail-import.test.tsx src/job-details-generated-drafts.test.tsx src/job-details-followup-drafts.test.tsx src/daily-control-loop.test.tsx`
### Build
- `cd job-tracker-ui && CI=true ./node_modules/.bin/react-scripts build`
## Observability / Diagnostic Surfaces Confirmed
The slice-plan observability surfaces are in place and useful:
- `GET /api/jobapplications/reminders`
- `GET /api/jobapplications/{id}/readiness`
- `GET /api/jobapplications/{id}/followup-draft`
- `GET /api/correspondence/{jobId}`
- `POST /api/gmail/refresh-linked-threads`
- `job-tracker-ui/src/jobWorkflowSignals.ts`
- `job-tracker-ui/src/workflow-trust-signals.test.tsx`
- `job-tracker-ui/src/end-to-end-trust-loop.test.tsx`
Backend tests proved normalized workflow-signal behavior. Frontend tests proved overview routing consistency and integrated trust-loop behavior. A live browser check also confirmed the app shell renders, but the current environment still has a CORS/runtime mismatch on `http://localhost:5202/api/...`, so full live UAT depends on running the backend with the expected CORS behavior.
## Requirement Impact
- **R010** is now validated. S05 proved coherent history and action continuity across overview routing, saved package reuse, linked Gmail updates, and follow-up drafting using one shared workflow contract.
- **R008** remains active by design. S05 re-proved the manual-send boundary, but the requirement stays active as an ongoing product constraint rather than a one-time feature box.
## Decisions / Gotchas Worth Carrying Forward
- Saved application answers should remain explicit workflow state, not inferred from generic notes.
- Linked-thread refresh state should stay visible in the main correspondence workspace.
- In this CRA frontend, run `react-scripts` from `job-tracker-ui/`; invoking via `npm --prefix ...` from repo root can mis-resolve the app directory and fail looking for a root-level `package.json`.
- Browser UAT still requires a correctly configured backend on port `5202`; otherwise the UI shell loads with CORS failures and empty data surfaces.
## What The Next Slice / Milestone Should Know
M001 is now assembled as one coherent single-user loop. Future work should build on the shared `workflowSignal` contract and the integrated trust-loop regression instead of adding new routing heuristics or duplicate readiness logic. If a later slice changes reminders, workspace entry, Gmail continuity, or follow-up drafting, it should update both the focused workflow-signal tests and the integrated trust-loop test together.
+218 -62
View File
@@ -1,90 +1,246 @@
# S05 Live-Safe UAT: Trust Loop Verification
# S05 UAT — End-to-end trust and workflow polish
## Goal
Verify the end-to-end trust loop on one real job without accidentally sending recruiter email.
Verify that one real job can be entered from `/jobs`, `/dashboard`, and `/reminders`, then carried through the same trusted workspace loop:
- saved package material is visible and reusable
- linked Gmail threads stay current without thread re-import
- follow-up drafting is grounded in saved package + correspondence
- no recruiter email is sent unless the human explicitly chooses the send action in a safe environment
## Safety Guardrails
1. **Do not press `Send and log email`** unless the environment is explicitly configured to a safe sink, stub mailbox, or other non-production outbound target.
2. If you are not certain the outbound email path is safe, stop after reviewing the generated follow-up draft.
3. Do not paste private recruiter or correspondence bodies into screenshots, tickets, or logs.
4. Prefer a job that already has:
- saved package material on the job,
- at least one linked Gmail thread,
- a follow-up reason/readiness signal.
1. **Do not click `Send and log email`** unless outbound mail is intentionally routed to a safe sink, stub mailbox, or other non-production target.
2. If safe outbound handling is not confirmed, stop after reviewing the follow-up draft and the manual-send boundary copy.
3. Do not capture or share screenshots containing sensitive recruiter or correspondence content.
4. Use one job that already has all or most of the following:
- a saved tailored CV and/or other saved package material
- at least one imported Gmail thread linked to the job
- a workflow action from the overview surfaces
- follow-up context that should produce a draft
## Preconditions
- The API and UI are running against the intended environment.
- API is running on the expected backend origin with working CORS for the frontend.
- UI is running and can load real API-backed job data.
- Gmail integration is authenticated for the current user.
- The selected job has an identifiable company, job title, and recruiter mailbox/thread history.
- If you plan to verify the send step, outbound email must point at a safe sink/stub. Otherwise stop before sending.
- The selected job belongs to the current user and has real recruiter/thread context.
- If the final send step will be tested, outbound mail is pointed at a safe sink/stub.
## Shared Expected Trust Signals
## Shared Expected Signals For The Same Job
The same job should present one coherent next action across all entry points:
Across `/jobs`, `/dashboard`, and `/reminders`, the same job should:
- `/jobs`
- `/dashboard`
- `/reminders`
- show the same general next action
- open the same job workspace
- land on the same tab or equivalent workflow destination
- preserve the same saved package, correspondence, and follow-up state
The workspace should make these states clear:
Inside the job workspace, expect to see:
- saved package material can be reused,
- linked Gmail refresh checks already-linked threads instead of requiring full re-import,
- follow-up draft generation is grounded in saved package + correspondence context,
- email sending remains manual.
- package save-state chips
- a signal that saved package material feeds follow-up drafting
- linked-thread continuity state in Correspondence
- explicit manual-send boundary copy in Follow up
## Flow A — Start from `/jobs`
---
## Test Case 1 — `/jobs` routes into the trusted workspace
### Steps
1. Open `/jobs`.
2. Find a job with a trust/workflow signal chip or next-action button.
3. Open the job via the primary next-action control.
4. Confirm the workspace opens on the expected tab for that action.
5. In **Tailored CV**:
- verify the saved tailored CV, cover letter, application answer, and recruiter message are present if the job previously had them,
- confirm the workspace explicitly says the saved package material feeds follow-up drafting.
6. In **Correspondence**:
- confirm the linked-thread continuity panel is visible,
- confirm Gmail connection state is shown,
- confirm linked thread count is shown when applicable,
- trigger **Refresh linked threads** if manual confirmation is needed,
- verify new linked correspondence appears without importing the full thread again.
7. In **Follow up**:
- confirm the draft context references saved package material and thread context,
- confirm the manual-send boundary copy is visible,
- review the generated draft but **stop before `Send and log email`** unless the environment is explicitly safe.
2. Find a job row with a workflow chip, readiness signal, or next-action control.
3. Open that job using the primary workflow action.
## Flow B — Start from `/dashboard`
### Expected
1. Open `/dashboard`.
2. Find the same job in an attention/reminder card.
3. Open it from the dashboard action.
4. Confirm it lands in the same job workspace and the same trust state is visible:
- package material is still saved,
- correspondence continuity is preserved,
- follow-up context remains grounded.
- The dialog/workspace opens for the selected job.
- The opened tab matches the jobs workflow need rather than a generic default.
- The selected company and role match the job row you opened.
## Flow C — Start from `/reminders`
---
1. Open `/reminders`.
2. Find the same job in the appropriate reminder grouping.
3. Open it from the reminder action.
4. Confirm it lands in the same workspace for the same job and preserves the same action semantics.
## Test Case 2 — saved package material is visible and reusable
### Steps
1. In the opened job workspace, go to **Tailored CV**.
2. Review the save-state chips for:
- Tailored CV
- Cover letter
- Application answer
- Recruiter message
3. Review the “Saved working material” panel.
4. If the job already has saved package data, confirm those fields show as saved.
### Expected
- The page shows save-state chips instead of leaving package trust implicit.
- The workspace explicitly indicates that saved package material feeds follow-up drafting.
- Saved material reflects the jobs current stored state, not just the latest generated draft.
- Resetting or revisiting the tab preserves the saved package state.
### Edge checks
- If one package field is unsaved, only that field should look incomplete; generic notes alone should not make the job appear package-ready.
- If the application answer exists, it should come back from saved state rather than disappearing or duplicating.
---
## Test Case 3 — correspondence shows linked-thread continuity in the workspace
### Steps
1. Open the **Correspondence** tab for the same job.
2. Confirm the Gmail connection state is visible.
3. Confirm the linked-thread panel/state is visible in the main workspace.
4. Review the linked-thread count.
5. Click **Refresh linked threads**.
### Expected
- The workspace shows Gmail connection status without needing the import modal to explain trust.
- If linked Gmail threads already exist, the workspace shows the linked-thread count.
- After refresh, the UI reports whether new messages were imported or whether linked threads were already current.
- The user is not forced through a full thread re-import flow for a thread that is already linked.
### Edge checks
- If no new Gmail messages exist, the refresh should say the linked threads are current rather than failing silently.
- If the job has no linked threads, the UI should say so clearly instead of implying a broken refresh.
---
## Test Case 4 — new linked correspondence appears without thread re-import
### Steps
1. Use a job whose recruiter thread has changed since the last import, or create that condition safely before the test.
2. With the same job open in **Correspondence**, trigger **Refresh linked threads**.
3. Review the correspondence timeline/list after refresh.
### Expected
- The new inbound or user-sent Gmail message appears in the same jobs correspondence.
- The refresh uses the already-linked Gmail thread.
- The user does not need to search for and re-import the whole thread manually.
### Edge checks
- Duplicate refreshes should not keep importing the same message repeatedly.
- Imported message order and thread continuity should still look sensible in the correspondence list.
---
## Test Case 5 — grounded follow-up drafting stays separate from send
### Steps
1. Open the **Follow up** tab for the same job.
2. Review the follow-up context panel.
3. Confirm it references package/correspondence grounding such as thread subject, last activity, or other grounding signals.
4. Click **Regenerate draft** if needed.
5. Review the manual-send boundary panel.
6. Edit the subject/body if desired.
7. **Stop before clicking `Send and log email`** unless outbound mail is confirmed safe.
### Expected
- The draft context clearly reflects saved package material and imported correspondence.
- Regenerating the draft changes/reloads draft content only; it does not send email.
- The manual-send boundary copy clearly states that generation/regeneration never sends recruiter email.
- The only outbound action remains the explicit send button.
### Edge checks
- If the recruiter email field is blank, drafting should still work; only the manual send step should be blocked or require completion.
- Draft review/editing should remain possible without side effects in correspondence.
---
## Test Case 6 — `/dashboard` opens the same job with the same semantics
### Steps
1. Close the workspace.
2. Open `/dashboard`.
3. Find the same job in an attention, readiness, or reminder card.
4. Open the job from the dashboard action.
### Expected
- The same job workspace opens.
- The opened tab/action meaning matches the jobs workflow need.
- Saved package state, linked-thread continuity state, and follow-up context are the same as when the job was opened from `/jobs`.
### Edge checks
- The dashboard should not route the same job to a conflicting tab/action compared with `/jobs`.
---
## Test Case 7 — `/reminders` opens the same job with the same semantics
### Steps
1. Close the workspace.
2. Open `/reminders`.
3. Find the same job in its reminder grouping.
4. Open the job from the reminder action.
### Expected
- The same job workspace opens.
- The same job lands in the same workflow area or equivalent action destination as the other entry points.
- Package state, correspondence continuity, and follow-up trust state remain unchanged.
### Edge checks
- Reminder grouping should reflect the same workflow classification seen elsewhere, not a conflicting heuristic.
---
## Test Case 8 — optional safe-sink send verification
> Run this only if outbound email is confirmed safe.
### Steps
1. Confirm the environment is using a stub mailbox, sink, or other non-production target.
2. In **Follow up**, click `Send and log email`.
3. Re-open **Correspondence** and/or refresh the job state.
### Expected
- The outbound action occurs only after the explicit click.
- The job history/correspondence reflects the sent follow-up appropriately.
- No autonomous send happened before this explicit action.
### Edge checks
- If the send endpoint is intentionally stubbed, the UI should still make the boundary clear and report the stubbed result consistently.
---
## Pass Criteria
- The same job can be opened from `/jobs`, `/dashboard`, and `/reminders`.
- The saved application package is visible and clearly reusable for follow-up drafting.
- Linked Gmail refresh shows continuity on already-linked threads without requiring a whole-thread re-import workflow.
- The follow-up draft is grounded in saved package and correspondence context.
- No recruiter email is sent unless the human explicitly chooses `Send and log email` in a safe environment.
S05 passes UAT when all of the following are true for the same job:
- `/jobs`, `/dashboard`, and `/reminders` all open the same job workspace coherently.
- Saved package material is visible as reusable workflow state.
- Linked-thread continuity is visible in the correspondence workspace.
- Refreshing linked threads updates correspondence without requiring re-import of an already-linked thread.
- Follow-up drafting is clearly grounded in package + correspondence context.
- Draft generation/regeneration never sends email on its own.
- No recruiter email is sent unless the human explicitly chooses the send action in a safe environment.
## Failure Clues
- Entry points route to different tabs or imply different next actions for the same job.
- Saved package material is missing or no longer called out as reusable context.
- Linked-thread refresh state is invisible or requires re-importing a thread that is already linked.
- Follow-up drafting triggers outbound send behavior or makes send/regenerate coupling unclear.
- Workspace state differs depending on whether the job was opened from jobs, dashboard, or reminders.
- Different surfaces route the same job to conflicting next actions.
- Generic notes make the job appear package-ready when saved package material is actually missing.
- Linked-thread freshness is hidden, ambiguous, or only discoverable through import-only UI.
- Refreshing linked threads requires a new full-thread import.
- Regenerating a follow-up draft appears coupled to sending.
- Workspace state changes depending on whether the job was opened from jobs, dashboard, or reminders.
@@ -0,0 +1,25 @@
{
"schemaVersion": 1,
"taskId": "T02",
"unitId": "M001/S05/T02",
"timestamp": 1774359406536,
"passed": false,
"discoverySource": "none",
"checks": [],
"retryAttempt": 1,
"maxRetries": 2,
"runtimeErrors": [
{
"source": "bg-shell",
"severity": "crash",
"message": "[jobtracker-api] exitCode=127",
"blocking": true
},
{
"source": "bg-shell",
"severity": "crash",
"message": "[jobtracker-api] exitCode=134 errors: --- End of inner exception stack trace ---; --- End of inner exception stack trace ---",
"blocking": true
}
]
}