mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
feat(coach): scope filter + conversation provenance on the plans list
Groundwork for the plan confirm/manage UI. The CoachPlan pipeline has been complete on the server since v1.21.3/v1.22 — extractor writes proposals (status "proposed", per-user cap, de-dup, encrypted prose), the PATCH confirms proposed -> active, buildCoachPlansBlock injects the top-6 active plans into the snapshot, and the review sweep drives the reviewDate lifecycle — but no surface ever listed or confirmed a proposal, so the extraction spend had no user-visible outcome. Two additive contract changes the UI needs: - GET /api/coach/plans accepts ?scope=open|past|all (open = proposed + active + review_due, past = met + abandoned + reviewed, all = no status clause) so the management page reads its ledger in one round-trip. Mutually exclusive with ?status=; both omitted keeps today's non-terminal default unchanged. - Plan items now carry sourceConversationId so the chat thread can show only the proposals born in the open conversation. OpenAPI regenerated; route tests cover the scope mappings, the status+scope 422, and the provenance passthrough.
This commit is contained in:
1 parent
4d0f16d5a9
commit
acd2f29da2
6 files changed
+152
-16
No files matched your search
@@ -17,10 +17,26 @@ import { z } from "zod/v4";
|
||||
|
||||
import { COACH_PLAN_STATUSES } from "@/lib/ai/coach/plans";
|
||||
|
||||
/** Optional `?status=` filter on the list endpoint. */
|
||||
export const coachPlansListQuerySchema = z.object({
|
||||
status: z.enum(COACH_PLAN_STATUSES).optional(),
|
||||
});
|
||||
/**
|
||||
* Optional `?scope=` filter on the list endpoint — a named status group so
|
||||
* the plans management surface can pull its whole ledger in one read:
|
||||
* - `open`: proposed + active + review_due (everything still standing)
|
||||
* - `past`: met + abandoned + reviewed (the settled history)
|
||||
* - `all`: every non-deleted plan
|
||||
* Additive next to `?status=`; the two are mutually exclusive per request.
|
||||
*/
|
||||
export const COACH_PLAN_SCOPES = ["open", "past", "all"] as const;
|
||||
|
||||
/** Optional `?status=` / `?scope=` filters on the list endpoint. */
|
||||
export const coachPlansListQuerySchema = z
|
||||
.object({
|
||||
status: z.enum(COACH_PLAN_STATUSES).optional(),
|
||||
scope: z.enum(COACH_PLAN_SCOPES).optional(),
|
||||
})
|
||||
.refine(
|
||||
(v) => !(v.status !== undefined && v.scope !== undefined),
|
||||
"Provide either status or scope, not both",
|
||||
);
|
||||
|
||||
/**
|
||||
* The user-facing PATCH body. `status` is the only mutable lifecycle field; a
|
||||
|
||||
Reference in new issue
Block a user