chore(M001/S05): auto-commit after complete-slice
This commit is contained in:
@@ -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 milestone’s 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.
|
||||
@@ -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 job’s 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 job’s 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 job’s 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 job’s 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
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user