2026-04-25

Module C (Customer) — Functional Decomposition

Artifact layer. Third of three Canary module artifact layers: 1. Canonical spec (vendor-neutral) — Canary-Retail-Brain/modules/C-customer.md 2. Code/schema crosswalk (Canary-specific) — Brain/wiki/canary-module-c-customer.md (planned for Q/T/R; exists for J/P/F/C) 3. Functional decomposition (Counterpoint-substrate-aware, L2/L3 + user stories) — this card

Governing thesis

R owns the People entity in CRDM, specifically the customer subset. Counterpoint exposes the richest People surface of any spine module — 17 endpoints covering AR_CUST + addresses + notes + cards + open AR + control + workgroup-driven defaults — and adds two capabilities Square has no equivalent for: multi-tier pricing identity (AR_CUST.CATEG_COD) and embedded loyalty (12 LOY_* fields per customer). For a Lawn & Garden tenant specifically, the multi-tier identity is the load-bearing feature: every Q rule that distinguishes wholesale from retail behavior depends on R, every P-derived pricing observation depends on R, every C-derived B2B classification depends on R.

R holds a deliberate inversion of the dominant industry default: the Canary v1 spec stores no PII at rest. Counterpoint, by contrast, holds full PII (name, email, phone, address, card-on-file, AR ledger, loyalty history). R's Counterpoint posture must reconcile these two: the Canary-side privacy commitment AND the Counterpoint-side full customer record. The reconciliation is read-through-to-Counterpoint at query time, not "ingest everything Counterpoint knows."

R is ● Full direct in every Counterpoint Solution Map cell, but the cell hides three real architectural decisions: (1) multi-company-per-tenant customer namespace handling, (2) the privacy-posture reconciliation just described, and (3) tier-code conventions that vary per-VAR and per-customer (every Counterpoint deployment uses CATEG_COD differently — there is no universal taxonomy).

Counterpoint Endpoint Substrate

Counterpoint Endpoint CRDM Entity L2 Process Area
AR_CUST Customer master C.1 (Customer identity), C.5 (Credit posture)
Customer/{CustNo}/OpenItems Open AR items C.3 (AR aging), C.4 (Payment history)
AR_CUST_CTL Credit control C.5 (Credit limit / tier)
PS_DOC_HDR (customer-linked) Transaction headers C.2 (Behavioral pattern), C.3 (Purchase history)
AR_CUST.IS_TAX_EXEMPT Tax exemption flag C.1 (Customer tax classification — sourced to F.2)

Executive summary

Dimension Count Source
L2 process areas 6 This card
L3 functional processes 32 This card
Counterpoint endpoints in R's path 17 (Customer family) API reference
AR_CUST loyalty fields 12 API reference + relationship wiki
Garden-center tier conventions enumerated 5 (walk-in, member, landscaper, project, wholesale) garden-center-operating-reality
Privacy-posture L3 processes 4 Q.4
Substrate contracts R owes downstream 8 This card §C.6
Assumptions requiring real-customer validation 9 Tagged ASSUMPTION-R-NN
User stories enumerated 47 Observer-perspective; actor ∈

Posture: archetype-shaped against Counterpoint specifically. The privacy-first v1 design (no PII at rest) is preserved. Per-VAR tier-code variance is acknowledged as a discovery surface at every customer onboarding — there is no universal CATEG_COD mapping.

L1 → L2 → L3 framework

L1 (Solution Map cell)         R = ● Full direct (Counterpoint Customer family — 17 endpoints)
                                 │
L2 (Process areas)               ├── C.1  Customer registry & upsert
                                 ├── C.2  Tier identity & multi-tier pricing context
                                 ├── C.3  Loyalty + AR ledger surfacing
                                 ├── C.4  Privacy posture (no PII at rest)
                                 ├── C.5  Identity resolution (cross-company, future cross-vendor)
                                 └── C.6  Investigator surface + downstream contracts
                                 │
L3 (Functional processes)       (32 — enumerated per L2 below)
                                 │
L4 (Implementation detail)      Lives in SDDs + module specs
                                  (Canary-Retail-Brain/modules/C-customer.md,
                                   docs/sdds/canary/ncr-counterpoint-retail-spine-integration.md §6.2)

C.1 — Customer registry & upsert

Purpose. Maintain one row per merchant per Counterpoint customer (per company alias, for multi-company tenants). T's parsed transaction stream triggers shell-row upserts before R has polled the Customer endpoint; R catches up async. The registry is the FK target every other module depends on for People references.

L3 processes

ID L3 process Substrate Notes
C.1.1 Shell-row upsert from T's reference T's transaction.created carrying CUST_NO Triggered before R has polled the customer record; row created with CUST_NO, merchant_id, company_alias, db_status='pending_enrichment' → TBD: L4 implementation detail pending
C.1.2 Full-row enrichment from GET /Customer/{CustNo} AR_CUST + nested AR_CUST_NOTE / AR_SHIP_ADRS / AR_CUST_CARDS Called when shell row needs enrichment OR on poll cadence → TBD: L4 implementation detail pending
C.1.3 Incremental sync via GET /Customers RS_UTC_DT-filtered paginated workhorse Counterpoint-recommended incremental path; respects watermark per (tenant, company_alias) → TBD: L4 implementation detail pending
C.1.4 EC-flagged customer enrichment GET /Customers/EC Online-customer subset; same registry, separate poll cadence → TBD: L4 implementation detail pending
C.1.5 CustomerControl read at tenant bootstrap GET /CustomerControl Tier definitions + loyalty enable + customer-default fields; cached server-side 24h; T.1.7 cache discipline applies → TBD: L4 implementation detail pending
C.1.6 Workgroup template read GET /Workgroup/{WorkgroupID} Numbering defaults + tier defaults that drive POST /Customer from the Counterpoint side → TBD: L4 implementation detail pending
C.1.7 Soft-delete on customer archival db_status='archived' rather than DELETE Preserves audit trail; re-activated on return, never duplicated → TBD: L4 implementation detail pending
C.1.8 Multi-company namespace isolation Per-(tenant_id, company_alias) registry partition One Canary tenant with N Counterpoint companies has N independent customer namespaces — never bleed across → TBD: L4 implementation detail pending

User stories

C.2 — Tier identity & multi-tier pricing context

Purpose. This is the load-bearing L&G capability. Counterpoint's AR_CUST.CATEG_COD is the multi-tier pricing identity field; for a garden-center tenant, the typical taxonomy is RETAIL / MEMBER / LANDSCAPER / PROJECT / WHOLESALE. Q.2.9 (customer-tier abuse), Q.6.x vertical-pack tier-aware allow-lists, and P-derived multi-tier pricing observations all depend on R surfacing the tier cleanly.

No universal taxonomy. Every Counterpoint deployment uses CATEG_COD differently. Per-VAR conventions vary; per-customer conventions vary within a VAR. This L2 surfaces the field; tier-meaning interpretation is a per-tenant configuration that gets captured at onboarding (see ASSUMPTION-R-03).

L3 processes

ID L3 process Substrate Notes
C.2.1 Surface CATEG_COD per customer AR_CUST.CATEG_COD Preserved verbatim; no normalization (different tenants use different code conventions) → TBD: L4 implementation detail pending
C.2.2 Tier-code → tier-meaning mapping per tenant Tenant config table; populated at onboarding E.g., WHL → wholesale, LSC → landscaper, MBR → member, RET → retail → TBD: L4 implementation detail pending
C.2.3 Tier-change audit on customer record AR_CUST.LST_MAINT_DT + LST_MAINT_USR_ID deltas Substrate for Q.2.9 (Q-CT-02 pre-purchase tier upgrade rule) → TBD: L4 implementation detail pending
C.2.4 Multi-tier pricing flag surfacing AR_CUST_CTL (CustomerControl) multi-tier flags Substrate for P-derived pricing rule observations → TBD: L4 implementation detail pending
C.2.5 Open-AR balance per customer GET /Customer/{CustNo}/OpenItems AR aging; substrate for C-derived B2B classification + Q-TC-02 (tax-exempt abuse adjacent) → TBD: L4 implementation detail pending
C.2.6 Customer credit posture AR_CUST.CR_RATE, NO_CR_LIM, BAL Substrate for C and risk-adjacent rules → TBD: L4 implementation detail pending
C.2.7 B2B vs retail derivation hooks Pattern-detect over C.2.1 + C.2.5 + transaction shape Feeds C-derived B2B classification; the C module is "derived from R" per Solution Map → TBD: L4 implementation detail pending

User stories

C.3 — Loyalty + AR ledger surfacing

Purpose. Counterpoint embeds 12 loyalty fields per customer (LOY_PGM_COD, LOY_PTS_BAL, LOY_CARD_NO, etc.) plus a full AR ledger via Customer_OpenItems. R surfaces both as substrate without persisting PII; loyalty + AR shape feeds C.6 contracts to downstream modules (Q for velocity rules, F for AR-vs-tender reconciliation, future C for B2B account behavior).

L3 processes

ID L3 process Substrate Notes
C.3.1 Surface loyalty enrollment AR_CUST.LOY_PGM_COD + LOY_CARD_NO (existence flags only — not card values) R stores the enrolled-yes/no flag, not the card number → TBD: L4 implementation detail pending
C.3.2 Surface loyalty balance AR_CUST.LOY_PTS_BAL (numeric only) Integer; safe to persist; substrate for repeat-purchase detection → TBD: L4 implementation detail pending
C.3.3 Surface loyalty redemption events Document line items with loyalty-redemption indicator Substrate for Q-related redemption-pattern rules; loyalty redemption captured in T's transaction stream → TBD: L4 implementation detail pending
C.3.4 Surface AR-customer flag AR_CUST.IS_AR_CUST or equivalent (ASSUMPTION-R-04) Distinguishes AR-charge-eligible customers from cash-only → TBD: L4 implementation detail pending
C.3.5 Surface open AR aging Customer_OpenItems aging buckets Substrate for F (AR collection workflows downstream) and C (B2B credit posture) → TBD: L4 implementation detail pending
C.3.6 AR-charge-vs-cash transaction posture Pattern-detect over C.3.4 + transaction tender mix Feeds Q-TM-01 (cash-only register pattern) — wholesale customers paying AR shift expected tender mix → TBD: L4 implementation detail pending

User stories

C.4 — Privacy posture (no PII at rest)

Purpose. R is the most architecturally opinionated module: the v1 implementation deliberately stores no PII at rest. Names, emails, phone numbers, addresses are intentionally not in R's tables. Counterpoint holds them; Canary reads at query time when the workflow demands it. This L2 owns that posture and the schema enforcement that makes it structural, not procedural.

Why this matters for Counterpoint. Square's customer surface is lighter. Counterpoint's AR_CUST carries deep PII (name, email, phone, multiple ship-to addresses, card-on-file, AR ledger, loyalty history). The naive integration would ingest all of it. R deliberately doesn't.

L3 processes

ID L3 process Substrate Notes
C.4.1 Schema-enforced PII absence customers table has no string columns for personal data Hard constraint at the DDL layer; can't be bypassed at application layer → TBD: L4 implementation detail pending
C.4.2 Read-through to Counterpoint at query time When workflow demands name / email / address: parser fetches from GET /Customer/{CustNo} per request No caching beyond request scope; never persisted → TBD: L4 implementation detail pending
C.4.3 Card-fingerprint storage (opaque) card_profiles holds Counterpoint's tokenized fingerprint (AR_CUST_CARDS token) Token, not PAN; not reversible in Canary → TBD: L4 implementation detail pending
C.4.4 PII-redaction-at-parse contract T.3.4 + T.7.10 redact SIG_IMG, SIG_IMG_VECTOR, raw PAN; R asserts compliance R never receives those bytes; T-side redaction is pre-condition → TBD: L4 implementation detail pending
C.4.5 GDPR/CCPA right-to-be-forgotten Soft-delete on R + vendor-side deletion request Single soft-delete suffices on Canary side; vendor (Counterpoint) handles its own → TBD: L4 implementation detail pending
C.4.6 Profile-extension opt-in (future / per-merchant flag) Per-tenant feature flag + extension table Default off; enabling requires explicit data-handling agreement; out of v1 → TBD: L4 implementation detail pending

User stories

C.5 — Identity resolution (cross-company, future cross-vendor)

Purpose. A single Canary tenant may run multiple Counterpoint companies (multi-company-per-tenant is real) and may eventually run Square-in-store + Counterpoint-online (cross-vendor). Today: per-(tenant, company_alias) namespaces are independent. v2: explicit identity resolution surface for cross-namespace customers.

L3 processes

ID L3 process Scope Notes
C.5.1 Per-(tenant, company_alias) namespace isolation Counterpoint multi-company today Same as C.1.8; no auto-merge across companies → TBD: L4 implementation detail pending
C.5.2 external_identities link table Cross-namespace identity scaffold Exists in Canary already (per GRO-267); links opt-in, never auto-derived → TBD: L4 implementation detail pending
C.5.3 Manual identity link surface Operator MCP tool "Link Counterpoint customer X in companyA to Counterpoint customer Y in companyB"; audit-logged, soft-revocable → TBD: L4 implementation detail pending
C.5.4 Cross-vendor identity resolution (v2) Square + Counterpoint same-customer matching Matching policy undecided: deterministic (email match) / probabilistic / customer-confirmed; out of v1 → TBD: L4 implementation detail pending
C.5.5 Customer-side ID assertion (future) Customer logs into Canary-merchant portal, asserts identity link Future surface; out of v1 → TBD: L4 implementation detail pending

User stories

C.6 — Investigator surface + downstream contracts

Purpose. R outputs feed two surfaces: (1) read-only customer queries from Owl/Fox/Operator MCP tools, and (2) substrate contracts to downstream modules. This L2 is symmetric to T.7 — same producer-view-of-contracts pattern.

L3 processes (investigator surface)

ID L3 process Surface Actor
C.6.1 Customer lookup by CUST_NO canary-identity MCP tool, read-only LP Analyst, Investigator → TBD: L4 implementation detail pending
C.6.2 Customer lookup by card fingerprint Same MCP, opaque token only Investigator → TBD: L4 implementation detail pending
C.6.3 Customer transaction history projection LTV / count / temporal bounds; aggregates derived from T LP Analyst, Store GM → TBD: L4 implementation detail pending
C.6.4 Owl natural-language Q&A over customers "Show me top wholesale customers by YTD revenue" Store GM, Exec → TBD: L4 implementation detail pending
C.6.5 Read-through-to-Counterpoint for PII-bearing fields At click time in Fox case view Investigator (audit-logged) → TBD: L4 implementation detail pending
C.6.6 Cohort projection (segment-by-tier, segment-by-LTV) Aggregates from C.2 + C.3 Marketing-adjacent (out of v1, deferred to v3) → TBD: L4 implementation detail pending

L3 contracts (substrate registry — symmetric to T.7)

ID Contract Owner downstream What R promises
C.6.7 Tier code surfaced verbatim Q (Q.2.9), C (derived) AR_CUST.CATEG_COD preserved exactly; tier-meaning mapping available per-tenant → TBD: L4 implementation detail pending
C.6.8 Tier-change audit Q (Q-CT-02) Tier deltas with timestamp + actor available as event substrate → TBD: L4 implementation detail pending
C.6.9 Loyalty enrollment + balance Q, repeat-purchase rules Integer balance + enrolled flag; no card numbers → TBD: L4 implementation detail pending
C.6.10 Open AR aging buckets F, C Per-customer aging without round-trip to Counterpoint → TBD: L4 implementation detail pending
C.6.11 Multi-tier pricing flag P (derived) AR_CUST_CTL multi-tier indicators surfaced for pricing-rule observation → TBD: L4 implementation detail pending
C.6.12 Tax-exempt customer flag Q (Q-TC-02) AR_CUST.IS_TAX_EXEMPT or equivalent (ASSUMPTION-R-06) surfaced as boolean → TBD: L4 implementation detail pending
C.6.13 Customer-namespace attribution All Every R reference carries (tenant_id, company_alias) — never bleed across companies → TBD: L4 implementation detail pending
C.6.14 PII-absence guarantee All Downstream consumers cannot pull PII from R; must read-through to Counterpoint via C.4.2 with audit → TBD: L4 implementation detail pending

User stories

Canary Detection Hooks

R Process → Detection Surface Signal Description
C.2 (Behavioral pattern routing) Q-IS rule family Customer behavioral anomalies (velocity spikes, unusual return patterns, cross-location activity) are published as Q-IS accumulation signals
C.5.3 (Cross-company customer collision) Q-DM-03 Customers detected under multiple company IDs with shared PAN or contact data are flagged to Q-DM-03 for identity-manipulation review
C.4 (Payment history) Q-TM rule family Unusual payment velocity or tender-mix for a known customer feeds Q-TM tender-monitoring rules

Additional User Stories

Assumptions requiring real-customer validation

These markers exist because the answer requires either a Rapid Garden POS sandbox database, a real customer's AR_CUST corpus, or both.

ID Assumption What it blocks Resolution path
ASSUMPTION-R-01 CUST_NO cardinality across companies — same value in companyA and companyB are different customers (assumed) C.1.8, C.5.1 namespace isolation correctness Sandbox multi-company test or docs confirmation
ASSUMPTION-R-02 "CASH" sentinel CUST_NO — Counterpoint convention for non-customer transactions C.1.1 shell-row upsert filtering (don't create CASH customer rows) Sandbox DB inspection — confirm sentinel value(s)
ASSUMPTION-R-03 Per-customer tier-code conventions — CATEG_COD value meanings (WHL, LSC, RET etc.) vary per VAR / per customer C.2.2 (tier-meaning mapping); every tier-aware Q rule Per-tenant onboarding discovery; no universal answer
ASSUMPTION-R-04 AR-customer flag field name — AR_CUST.IS_AR_CUST assumed; not directly visible in current sample C.3.4 substrate path Sandbox DB schema inspection
ASSUMPTION-R-05 Loyalty enrollment indicators — which of the 12 LOY_* fields are the boolean enrollment vs balance vs card C.3.1, C.3.2 surface decisions Sandbox sample data + AR_CUST schema docs
ASSUMPTION-R-06 Tax-exempt customer field name — likely IS_TAX_EXEMPT or via TAX_COD = NOTAX; needs confirmation C.6.12 contract; Q-TC-02 substrate Sandbox DB inspection
ASSUMPTION-R-07 EC (eCommerce) customer relationship — is GET /Customers/EC a filter view of GET /Customers or a separate table C.1.4 vs C.1.3 implementation Sandbox endpoint inspection
ASSUMPTION-R-08 Workgroup customer-template scope — does Workgroup affect customer reads or only customer creation defaults C.1.6 — may or may not be in steady-state read path Documentation read or sandbox test
ASSUMPTION-R-09 Multi-name plant convention impact on R — none expected (item-side, not customer-side), but flagged in case Rapid POS extends customer record with garden-specific fields C.4.1 schema-enforced PII absence — extensions could violate without explicit migration Per-customer at onboarding

Highest-leverage gaps: R-03 (tier-code conventions) — load-bearing for every L&G-distinctive Q rule. Cannot be assumed at platform level; must be discovered per-tenant. Capture as part of CATz Phase II To-Be Workshop output.

Customer-specific overrides

Empty until a real customer engagement starts. Format reserved:

Customer: <name>
Counterpoint deployment: <single-company | multi-company (N companies)>
Company aliases: [<alias1>, <alias2>, ...]
Tier-code mapping (C.2.2):
  CATEG_COD value | meaning
  ---             | ---
  WHL             | wholesale
  LSC             | landscaper
  MBR             | member
  RET             | retail
  PRJ             | project (one-time landscape)
  ...
Loyalty program in use: <yes — program name | no>
Loyalty enrollment field convention: <which LOY_* field>
AR ledger active: <yes | no>
Tax-exempt convention: <field name + value convention>
Profile-extension opt-in: <off — default | on — DPA reference>
Disabled R.x processes (with reason):
  R.x.x: <reason>
ASSUMPTION resolutions:
  ASSUMPTION-R-NN: resolved as <answer>; source: <evidence>
  ...
Manual identity links (C.5.3):
  <link entry list>

Operating notes