diff --git a/docs/superpowers/plans/2026-08-29-order-quote-linking.md b/docs/superpowers/plans/2026-08-29-order-quote-linking.md index ad1571b..255107d 100644 --- a/docs/superpowers/plans/2026-08-29-order-quote-linking.md +++ b/docs/superpowers/plans/2026-08-29-order-quote-linking.md @@ -1,5 +1,21 @@ # Order ↔ Quote Auto-Linking Implementation Plan +> ## ⚠️ STOP — Tasks 1–6 are already implemented and merged +> +> **Read `docs/superpowers/plans/2026-08-29-HANDOFF-outstanding-work.md` FIRST.** +> +> Tasks 1–6 below are done. Only **Tasks 7–13** remain, plus two follow-up +> fixes to Task 6 that are described only in the handoff doc. +> +> Six review cycles changed things this document still describes the old way. +> The handoff lists every correction. The most dangerous ones fail **silently** +> — `vitest.config.ts` has no `clearMocks` so mock history leaks between tests +> and produces green-but-vacuous tests; hand-built PocketBase OR-chain filters +> 400 past ~3.5KB; field projections are load-bearing and untested projections +> silently disable safety guards. +> +> Where this plan and the handoff disagree, **the handoff wins**. + > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Every incoming order is automatically matched to its originating quote as a one-click-confirmable suggestion, and each order line shows the buyer note and attachments from its paired quote line. @@ -567,7 +583,10 @@ export interface SuggestQuoteResult { alternatives: QuoteMatchCandidate[]; method: QuoteMatchMethod; /** Which pass produced the result — for the sweep's reporting. */ - window: "exact" | "90d" | "widened" | "none"; + // NOTE: shipped as "initial", not "90d" — review renamed it so that + // retuning FUZZY_WINDOW_DAYS can't make the label lie. The real shape is + // `"exact" | "initial" | "widened" | "none"`. See the handoff doc. + window: "exact" | "initial" | "widened" | "none"; /** Days from the matched quote's creation to the order's. null when unmatched. */ ageGapDays: number | null; /** Set when the order was deliberately not processed. */ @@ -775,7 +794,7 @@ export async function suggestQuoteForOrder( }); let scored = await fuzzyPass(pb, order, orderItems, FUZZY_WINDOW_DAYS); - let window: SuggestQuoteResult["window"] = "90d"; + let window: SuggestQuoteResult["window"] = "initial"; // shipped value; not "90d" if (scored.length === 0) { scored = await fuzzyPass(pb, order, orderItems, FUZZY_WINDOW_WIDEN_DAYS); window = "widened"; @@ -1326,7 +1345,7 @@ describe("sweepUnmatchedOrders", () => { items: [{ id: "o1" }, { id: "o2" }], totalItems: 2, }); - useP(makePb({ [ORDERS]: { getList, getOne: vi.fn(), update: vi.fn() } })); + mockAdminPb(makePb({ [ORDERS]: { getList, getOne: vi.fn(), update: vi.fn() } })); await sweepUnmatchedOrders({ limit: 50 }); @@ -1356,7 +1375,7 @@ describe("sweepUnmatchedOrders", () => { id: "good", created: "2026-08-29T10:00:00.000Z", company: "co1", customer: "", quote: "", }); - useP( + mockAdminPb( makePb({ [ORDERS]: { getList, getOne, update: vi.fn().mockResolvedValue({}) }, [QUOTES]: { getList: vi.fn().mockResolvedValue({ items: [] }) },