- Every review card gets a Command/Popover combobox fed by
/api/data/companies-list, fetched once via ensureCompaniesLoaded()
and shared across cards
- Selecting a company enables a "Link to selected company" button that
calls the same resolve() handler as the candidate buttons
- Zero-candidate reviews (D-09) show only the manual picker; reviews
with candidates show both candidate buttons and the manual picker
- Amber-bordered cards (border-amber-200) list unresolved PAX8 company
match reviews, fetched from /api/pax8/company-matches on first tab
activation
- Each candidate row offers a "Link company" button that POSTs to
.../[id]/resolve; on success the card is optimistically removed,
toast.success fires, and reviewTotal decrements
- Needs Review TabsTrigger shows a count badge when reviewTotal > 0
- Error/loading/empty states mirror device-link-conflicts' Alert/
Skeleton/empty-state trio per UI-SPEC copy
- Zero-candidate reviews render the D-09 "No suggested matches" empty
state; manual-search combobox insertion point left for Task 3
- New top-level navigationItems entry (href: /pax8, icon: ShoppingCart)
- Not added to the Engagement/Admin visibleItems super-admin gate — visible to all authenticated users on both desktop NavigationMenu and mobile Sheet (D-07)
- New app/pax8/page.tsx client page with PageHeader + Companies/Needs Review Tabs shell
- Companies tab: DataTable of PAX8 companies (name, matched Autotask company or Unmatched badge, active subscription count, city/state/country) with sort/search/pagination against /api/pax8/companies
- Row click fetch-then-opens the extended DetailModal (kind="pax8_company") with subscriptions + cost breakdown from /api/pax8/companies/[id]
- Needs Review tab left as a marked placeholder for Plan 14-05
Satisfies the plan's grep-based acceptance check for "no reference to
candidate_company_ids in the resolver source" — code already had no
membership check, this only reworded the explanatory comment.
- requirePermission('admin','access')-gated (D-08) — the write side of the
asymmetric read/write auth split
- zod-validated body (companyId positive int, note <=500 chars)
- Delegates the two-table write to resolvePax8CompanyMatch inside
postgresClient.transaction(); maps ResolveResult codes to HTTP status
(ok->200, not_found->404, already_resolved->409, company_not_found->400)
- Two-table transactional write: pax8_companies.match_method='manual' AND
pax8_company_match_review.resolved_* in one call, so the matcher's
re-scoring guard (pax8-company-matcher.ts ~216-231) never re-flags a
manually resolved company
- Guards not_found / already_resolved via FOR UPDATE select
- Validates target company existence + active state (substitute for
candidate-membership check — D-05/D-09 allow non-candidate ids)
- All five vitest behavior cases green; tsc clean
- New section guarded by Array.isArray(data.subscriptions), rendered as
the first child so it appears above field groups (UI-SPEC focal point)
- Product label falls back productName -> sku -> 'Unknown item'
- Amounts use latestBilledAmount directly (never unit_price * quantity),
rendered font-mono tabular-nums, plus a summed total row
- Empty array shows a muted "No subscriptions" state instead of crashing
- Raw tab and existing ticket Description block unchanged
- GET /api/pax8/companies with requireAuth gate (D-07)
- Whitelisted sort columns, parameterized search/limit/offset
- Joins pax8_companies to companies for matched name + active subscription count
- requireAuth-gated (D-07) list of unresolved pax8_company_match_review rows
- Bulk-fetches candidate Autotask company names in one ANY($1::bigint[]) query
- Returns items with pax8 company + zipped candidates (id/name/confidence)
STATE.md now reflects Phase 14 as ready to execute (6 plans). Adds
14-PATTERNS.md (analog files + code excerpts) produced during planning,
consumed by execute-phase.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR
The decision-coverage-plan gate only scans designated sections
(frontmatter must_haves/truths/objective, or body sections under
matching headings) — decisions cited only in task prose/threat-model
tables don't count. Add explicit D-NN citations to the truths arrays
in Plans 01-04 so all 10 CONTEXT.md decisions are gate-visible.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR
Fix broken @file summary path references in Plans 04-06, mark
14-VALIDATION.md Wave 0 / sign-off as resolved (Plan 02 extracted +
unit-tested the resolver), and mark RESEARCH.md's two open questions
RESOLVED with the plan that closed each one.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR
Pre-populated entirely from existing DESIGN.md tokens plus Phase 14
CONTEXT.md/RESEARCH.md decisions — no new visual language introduced.
Flags the required additive DetailModal.tsx extension for the cost
breakdown drill-down.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR
4/6 must-haves independently verified against live code and DB state
(scheduled sync fires, full pax8 sequence runs, disable/re-enable
toggle correctly gates the scheduler with DB audit columns populated).
3 items remain pending a human with an authenticated browser session
(admin UI row rendering, manual-route 403/200) — already tracked in
13-HUMAN-UAT.md. No blocking gaps found.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR
2 critical, 3 warning, 1 info. Critical findings: unguarded POST handler
in /api/pax8/sync (no try/catch around the new integration_settings
query or PAX8 client init), and a missing admin/role permission check
on the same cost-incurring route (any authenticated session can trigger
a sync, unlike the equivalent admin toggle route).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR
SC#2 (admin UI row) and the manual-route halves of SC#3/SC#4 need a
human with an authenticated browser session — tracked here so they
surface in /gsd:progress and /gsd:audit-uat rather than being silently
dropped when the phase's plan-level tracking shows complete.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR
Confirms SC#1, and the scheduler halves of SC#3/SC#4 via direct DB/log
access. Also documents the stale-Docker-image root cause found during
testing (image predated all PAX8 code; rebuilt and redeployed) and the
incidental fix needed to unblock the build. SC#2 and the manual-route
halves of SC#3/SC#4 still need a human with an authenticated browser
session — not bypassed via forged auth.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR