toga-ai 1.0.469 → 1.0.470
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -6,7 +6,7 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-29
|
|
10
10
|
owners: ["mhammontree"]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Component/Api/V2/V2.php
|
|
@@ -66,8 +66,39 @@ fact AIG **does** post live entitlements — the writes were **failing (HTTP 500
|
|
|
66
66
|
core `Logs`**, not `Logs_Aig`. See
|
|
67
67
|
[AIG entitlement intake → Intake is a live API feed](../../../../clients/aig/features/entitlement-intake.md).
|
|
68
68
|
|
|
69
|
+
## Recovering an intake backlog from logged payloads
|
|
70
|
+
|
|
71
|
+
Because the core/writer `Logs.Api` stores **every** inbound request — including failed writes —
|
|
72
|
+
each row retains the **complete original `requestPayload`** (full contact / device / pricing /
|
|
73
|
+
coverage). That makes the log a **replayable source of truth** for a client's dropped-intake
|
|
74
|
+
backlog and a diagnostic for *which* upstream programs actually attempted intake:
|
|
75
|
+
|
|
76
|
+
- **Rebuild the backlog** by replaying the stored `requestPayload`s, **deduped to one row per
|
|
77
|
+
contract** via a MySQL 8 window function keyed on
|
|
78
|
+
`JSON_UNQUOTE(JSON_EXTRACT(requestPayload,'$.number'))` (many failed attempts exist per contract
|
|
79
|
+
when a feed re-sends — see below).
|
|
80
|
+
- **Determine which programs/vendors reached the API** with a split on
|
|
81
|
+
`JSON_UNQUOTE(JSON_EXTRACT(requestPayload,'$.vendor.number'))` — a vendor present in the failed
|
|
82
|
+
writes attempted intake; a vendor entirely absent never routed to api2 at all.
|
|
83
|
+
|
|
84
|
+
This is both a **fallback recovery path** and a **scope diagnostic**. In the TRUE-80179 AIG
|
|
85
|
+
incident it answered the scope question — the failed writes contained only vendor `STS_001` and
|
|
86
|
+
**zero `SA_001`**, proving `SA_001` never reached api2 — while the *replay* itself was ultimately
|
|
87
|
+
**not needed**, because AIG's scheduled feed re-sent the outstanding contracts and self-healed the
|
|
88
|
+
backlog once intake succeeded. Keep the replay technique as the fallback for feeds that do **not**
|
|
89
|
+
re-send.
|
|
90
|
+
|
|
91
|
+
> **Never copy a logged row's values into a doc or a replay artifact you commit** — these rows
|
|
92
|
+
> carry live Bearer tokens (in `requestHeaders`) and customer PII. Reference the row by location;
|
|
93
|
+
> replay operationally, not by pasting payloads into the KB.
|
|
94
|
+
|
|
69
95
|
## Change history
|
|
70
96
|
|
|
97
|
+
- 2026-07-29 — Documented (TRUE-80179) that the core/writer `Logs.Api` retains the **full original
|
|
98
|
+
`requestPayload`** on failed writes, making it a **replayable backlog source** and a scope
|
|
99
|
+
diagnostic: dedupe to one-per-contract with a window function on
|
|
100
|
+
`JSON_EXTRACT(requestPayload,'$.number')`, and split on `$.vendor.number` to see which programs
|
|
101
|
+
actually attempted intake. (mhammontree)
|
|
71
102
|
- 2026-07-28 — Refined (TRUE-80179): a **failed write (non-2xx `POST`/`PUT`) lands in the core/writer
|
|
72
103
|
`Logs`** on `api-writer.togahub.com` **even when the request is fully client-scoped** — not just
|
|
73
104
|
pre-scope auth failures. `Logs_<Client>.Api` therefore shows reads + successful writes; failed
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
| Doc | Framework | Summary | Files |
|
|
4
4
|
|-----|-----------|---------|-------|
|
|
5
|
-
| [AIG Contract Reconciliation & Dealer Programs (STS_001 / SA_001)](features/contract-reconciliation.md) | 2.0 | How to reconcile an **AIG contract sales sheet** against the `Client_Aig` tenant, and the durable finding that the **Staples Advantage.com (`SA_001`) program
|
|
5
|
+
| [AIG Contract Reconciliation & Dealer Programs (STS_001 / SA_001)](features/contract-reconciliation.md) | 2.0 | How to reconcile an **AIG contract sales sheet** against the `Client_Aig` tenant, and the durable finding that the **Staples Advantage.com (`SA_001`) program do | _underscore/Model/Client/Entitlement.php, _underscore/Model/Aig/Entitlement.php, _underscore/Model/Client/Contact.php, _underscore/Model/Aig/Contact.php |
|
|
6
6
|
| [AIG Entitlement Intake & SaleItem Code Resolution](features/entitlement-intake.md) | 2.0 | The `_Model_Aig_Entitlement::prePost` interceptor below is the code path for AIG protection-plan entitlements arriving as `api2` V2 JSON POSTs — and that path * | _underscore/Model/Aig/Entitlement.php, dbchanges2/Client_Aig/2026-06-18a - TRUE-79534 AIG SaleItem codes.sql |
|
|
7
7
|
| [AIG (Staples Protection Plan)](profile.md) | 2.0 | AIG is the warranty underwriter behind the **Staples Protection Plan** retail program. | |
|
|
@@ -5,7 +5,7 @@ project: API
|
|
|
5
5
|
client: aig
|
|
6
6
|
type: client-feature
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-07-
|
|
8
|
+
updated: 2026-07-29
|
|
9
9
|
owners: ["mhammontree"]
|
|
10
10
|
files:
|
|
11
11
|
- _underscore/Model/Client/Entitlement.php
|
|
@@ -20,17 +20,18 @@ related:
|
|
|
20
20
|
## Summary
|
|
21
21
|
|
|
22
22
|
How to reconcile an **AIG contract sales sheet** against the `Client_Aig` tenant, and the
|
|
23
|
-
durable finding that the **Staples Advantage.com (`SA_001`) program
|
|
24
|
-
`Client_Aig
|
|
25
|
-
tenant**, distinguished by `DealerId` in AIG's own data:
|
|
23
|
+
durable finding that the **Staples Advantage.com (`SA_001`) program does not route into
|
|
24
|
+
`Client_Aig` via api2 at all** (TRUE-80179). AIG runs **two dealer programs under the one
|
|
25
|
+
`Client_Aig` tenant**, distinguished by `DealerId` in AIG's own data:
|
|
26
26
|
|
|
27
|
-
| DealerId | Program |
|
|
28
|
-
|
|
29
|
-
| `STS_001` | **Staples Technology Solutions** |
|
|
30
|
-
| `SA_001` | **Staples Advantage.com** |
|
|
27
|
+
| DealerId | Program | Status |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `STS_001` | **Staples Technology Solutions** | live api2 feed; intake gap **RESOLVED 2026-07-29** (self-healed) |
|
|
30
|
+
| `SA_001` | **Staples Advantage.com** | **never reaches api2** — separate feed; scope decision is the one open item |
|
|
31
31
|
|
|
32
|
-
`STS_001` arrives as a **live API feed
|
|
33
|
-
|
|
32
|
+
`STS_001` arrives as a **live API feed**; its 156-contract gap from the now-fixed intake bug
|
|
33
|
+
**self-healed** (the feed re-sent and all 156 succeeded — see below). `SA_001` never reaches our
|
|
34
|
+
API at all — a confirmed fact, not a missing-intake bug (see the `SA_001` section).
|
|
34
35
|
|
|
35
36
|
## Match keys (sales sheet → `Client_Aig`)
|
|
36
37
|
|
|
@@ -70,41 +71,62 @@ and dropping every contract that used an `ASI-*` code. Cause: the TRUE-79534 `AS
|
|
|
70
71
|
query threw MySQL ERROR 1064 → 500. Full chain in
|
|
71
72
|
[entitlement-intake → Root cause of the 500s](entitlement-intake.md#root-cause-of-the-intake-500s-true-79534-catalog-never-deployed).
|
|
72
73
|
|
|
73
|
-
**
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
74
|
+
**RESOLVED 2026-07-29 — the 156 self-healed.** After the catalog (~2,200 codes) loaded on prod, AIG's
|
|
75
|
+
**next scheduled batch re-sent all 156 outstanding `STS_001` contracts and every one succeeded**
|
|
76
|
+
(`POST /v2/entitlements` = 201 × 156, zero failures; `Client_Aig.Entitlements` gained exactly 156
|
|
77
|
+
rows). **No manual back-fill was needed** — the feed re-sends OUTSTANDING contracts every run, so
|
|
78
|
+
the backlog cleared itself once intake worked (see
|
|
79
|
+
[entitlement-intake → scheduled re-send batch](entitlement-intake.md#feed-is-a-scheduled-re-send-batch-not-one-shot--self-healing)).
|
|
80
|
+
The `STS_001` intake failure mode is **RESOLVED in prod.**
|
|
81
|
+
|
|
82
|
+
**Still open (tickets, not doc work):**
|
|
83
|
+
- **(a) Unguarded empty-`$itemId` in `prePost`** — a still-missing sale-item code will 500 again
|
|
84
|
+
(ERROR 1064) instead of returning a clean "Missing AIG item ID." Latent until guarded.
|
|
85
|
+
- **(b) Deploy process gap** — investigate *why* the TRUE-79534 migration SQL step was skipped in
|
|
86
|
+
the deploy, so future `dbchanges2` changes aren't silently missed.
|
|
82
87
|
|
|
83
|
-
### 545 `SA_001` missing —
|
|
88
|
+
### 545 `SA_001` missing — confirmed out-of-pipeline (separate feed)
|
|
84
89
|
|
|
85
90
|
The entire `SA_001` (Staples Advantage.com) feed is **absent from `Client_Aig`** — confirmed
|
|
86
91
|
absent from **all** local tenant DBs, not just this one — and is a **separate** matter from the
|
|
87
92
|
`STS_001` catalog bug above.
|
|
88
93
|
|
|
94
|
+
**CONFIRMED (2026-07-29): `SA_001` never reaches our API at all.** The failed-POST logs and the
|
|
95
|
+
vendor split (on `$.vendor.number`) show **only `STS_001`** (100 unique failed contracts) and
|
|
96
|
+
**zero `SA_001`** across **all** response codes. So the 545 `SA_001` contracts (506 active) are a
|
|
97
|
+
**genuinely separate feed that does not route into `Client_Aig` via api2** — this is **not** a
|
|
98
|
+
missing-intake bug on our side. The long-open scope question is now a **decided fact**: `SA_001` is
|
|
99
|
+
out-of-pipeline. Do **not** re-investigate whether it is a missing-intake defect.
|
|
100
|
+
|
|
89
101
|
**Real support failure that surfaced it:** a customer (client contract `7679463206-2`, AIG
|
|
90
102
|
contract `1000046237903`, an **active 2YR Printer** plan) called in and could not be found,
|
|
91
103
|
because their `SA_001` contract was never ingested.
|
|
92
104
|
|
|
93
|
-
**
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
**The one remaining open item (a ticket/business decision, not a bug):** *should* `SA_001` flow
|
|
106
|
+
into `Client_Aig`, and if so via what feed/integration? That is a scope/integration decision for
|
|
107
|
+
AIG / the team — not a back-fill of a dropped load. Until decided, `SA_001` stays out-of-pipeline.
|
|
96
108
|
|
|
97
109
|
## How to load a missing program / sheet (back-fill)
|
|
98
110
|
|
|
99
|
-
If `SA_001`
|
|
100
|
-
|
|
101
|
-
|
|
111
|
+
If `SA_001` is ever confirmed in-scope for `Client_Aig` and AIG cannot route it through api2, it
|
|
112
|
+
can be loaded the same way the SaleItem catalog is — a `dbchanges2/Client_Aig/` migration with a
|
|
113
|
+
session-scoped temp table + anti-join
|
|
102
114
|
(see [entitlement-intake.md → Uploading new codes](entitlement-intake.md#uploading-new-codes-the-recurring-task);
|
|
103
|
-
**pin the temp column's collation** to avoid the prod-only ERROR 1267).
|
|
104
|
-
|
|
115
|
+
**pin the temp column's collation** to avoid the prod-only ERROR 1267). Note the `STS_001` backlog
|
|
116
|
+
did **not** need this — a scheduled re-send self-healed it once intake worked, so prefer letting a
|
|
117
|
+
feed re-send over a manual load whenever the contracts do reach api2.
|
|
105
118
|
|
|
106
119
|
## Change history
|
|
107
120
|
|
|
121
|
+
- 2026-07-29 — TRUE-80179 / TRUE-79534: **156 active `STS_001` RESOLVED (self-healed).** The next
|
|
122
|
+
scheduled batch re-sent all 156 outstanding contracts and every one succeeded (201 × 156, zero
|
|
123
|
+
failures; `Client_Aig.Entitlements` +156) — no manual back-fill needed, because the feed re-sends
|
|
124
|
+
outstanding contracts every run. Also **confirmed `SA_001` never reaches api2**: the failed-POST
|
|
125
|
+
logs / vendor split show only `STS_001` (100 unique failed) and zero `SA_001` across all codes, so
|
|
126
|
+
the 545 `SA_001` (506 active) are a genuinely separate out-of-pipeline feed — converting the old
|
|
127
|
+
scope question into a decided fact (not a missing-intake bug). Remaining open items are tickets:
|
|
128
|
+
the `SA_001` scope/integration decision, the unguarded `prePost` lookup, and the skipped-migration
|
|
129
|
+
deploy gap. (mhammontree)
|
|
108
130
|
- 2026-07-28 — TRUE-80179 / TRUE-79534: root-caused the **156 missing active `STS_001`**. These are
|
|
109
131
|
a **live API feed** whose writes were 500'ing (ERROR 1064) and being dropped because the
|
|
110
132
|
TRUE-79534 `ASI-*`/`SM-*` catalog never deployed to prod `Client_Aig.Items` — corrects the earlier
|
|
@@ -5,7 +5,7 @@ project: API
|
|
|
5
5
|
client: aig
|
|
6
6
|
type: client-feature
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-07-
|
|
8
|
+
updated: 2026-07-29
|
|
9
9
|
owners: ["mhammontree"]
|
|
10
10
|
files:
|
|
11
11
|
- _underscore/Model/Aig/Entitlement.php
|
|
@@ -25,13 +25,17 @@ The `_Model_Aig_Entitlement::prePost` interceptor below is the code path for AIG
|
|
|
25
25
|
protection-plan entitlements arriving as `api2` V2 JSON POSTs — and that path **is a live
|
|
26
26
|
production feed.** AIG posts real customer contracts to `POST /v2/entitlements` on
|
|
27
27
|
`api-writer.togahub.com` (authenticated as client `Aig`, vendor `STS_001`) using the current
|
|
28
|
-
`ASI-*` / `SM-*` sale-item codes.
|
|
29
|
-
500** because `Client_Aig.Items` was missing the entire
|
|
30
|
-
load never ran on prod), so every posted contract was
|
|
31
|
-
|
|
32
|
-
and
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
`ASI-*` / `SM-*` sale-item codes. From before 2026-07-28 until the catalog was loaded, that feed
|
|
29
|
+
was **silently failing with HTTP 500** because `Client_Aig.Items` was missing the entire
|
|
30
|
+
`ASI-*`/`SM-*` catalog (the TRUE-79534 load never ran on prod), so every posted contract was
|
|
31
|
+
dropped at intake. **RESOLVED 2026-07-29** — once the catalog loaded, AIG's next scheduled batch
|
|
32
|
+
delivered all **156** outstanding `STS_001` contracts and **every one succeeded** (`POST
|
|
33
|
+
/v2/entitlements` = **201 × 156**, zero failures; `Client_Aig.Entitlements` gained exactly 156
|
|
34
|
+
rows). **No manual back-fill was needed — the feed self-healed** (see
|
|
35
|
+
[Intake is a live API feed](#intake-is-a-live-api-feed-resolved-2026-07-29-true-80179--true-79534)
|
|
36
|
+
and [Feed is a scheduled re-send batch](#feed-is-a-scheduled-re-send-batch-not-one-shot--self-healing)).
|
|
37
|
+
The historical **430** `Client_Aig` entitlements were a one-time Sept-2025 bulk load — **that part
|
|
38
|
+
stands** — but AIG has since switched to the live API feed.
|
|
35
39
|
|
|
36
40
|
On a V2 POST, before the entitlement is written, `prePost` resolves the **sold warranty SKU**
|
|
37
41
|
(`saleItem.partNumber`) against `Client_Aig.Items` to set the `Entitlement.saleItemId` foreign
|
|
@@ -40,7 +44,7 @@ the lookup returns an **empty `$itemId`**, the downstream fulfillment query inte
|
|
|
40
44
|
empty value, and it dies with MySQL **ERROR 1064** → **HTTP 500** (see the gotcha). Keeping
|
|
41
45
|
`Client_Aig.Items` populated with AIG's current sale-item catalog is what keeps the feed working.
|
|
42
46
|
|
|
43
|
-
## Intake is a live API feed (
|
|
47
|
+
## Intake is a live API feed (RESOLVED 2026-07-29) (TRUE-80179 / TRUE-79534)
|
|
44
48
|
|
|
45
49
|
The API-intake path in *How it works* is the **real, live feed.** An earlier capture wrongly
|
|
46
50
|
concluded "no live feed — periodic bulk load only." That conclusion came from auditing
|
|
@@ -65,10 +69,34 @@ What the corrected audit found (TRUE-80179):
|
|
|
65
69
|
contracts in [reconciliation](contract-reconciliation.md), and it kept losing contracts **daily**
|
|
66
70
|
until the catalog was loaded on 2026-07-28.
|
|
67
71
|
|
|
72
|
+
**Resolution (2026-07-29):** once the catalog was loaded (TRUE-79534) and the temp-table collation
|
|
73
|
+
fixed, AIG's **next scheduled batch cleared the entire backlog in one pass** — `Logs_Aig` shows
|
|
74
|
+
`POST /v2/entitlements` = **201 × 156** on 2026-07-29 with **zero failures** in the core/writer
|
|
75
|
+
`Logs`, and `Client_Aig.Entitlements` gained exactly the **156** previously-missing active
|
|
76
|
+
`STS_001` rows. **No manual log-replay was needed.** The prior "intake 500s / Missing AIG item ID"
|
|
77
|
+
failure mode is **RESOLVED in prod for the STS_001 side**, which is now healthy and self-maintaining
|
|
78
|
+
(see next section for *why* it self-healed).
|
|
79
|
+
|
|
68
80
|
**Practical takeaway:** AIG intake is a **live API feed whose health must be monitored** — not a
|
|
69
81
|
manual bulk load, and not a phantom "no feed." When auditing it, check the **core/writer `Logs`**
|
|
70
82
|
for non-2xx writes, not just `Logs_Aig`. For reconciling a sales sheet against the tenant (and the
|
|
71
|
-
|
|
83
|
+
one remaining `SA_001` scope decision), see [AIG contract reconciliation](contract-reconciliation.md).
|
|
84
|
+
|
|
85
|
+
## Feed is a scheduled re-send batch (not one-shot) → self-healing
|
|
86
|
+
|
|
87
|
+
**Correction of an interim read.** AIG's entitlement feed is a **scheduled batch that re-sends
|
|
88
|
+
OUTSTANDING / unacknowledged contracts every run** — it is **not** one-shot-per-contract, and it
|
|
89
|
+
does **not** "not retry." Supersedes any earlier note implying the feed does not retry.
|
|
90
|
+
|
|
91
|
+
Evidence: the same ~156 contracts were **re-POSTed on every run** and 500'd on the missing catalog —
|
|
92
|
+
that is what produced **~110,504 failed attempts for only ~100 distinct contract numbers** in the
|
|
93
|
+
logs. The moment the catalog resolved, the **very next scheduled run cleared all 156 in a single
|
|
94
|
+
pass** (156 × 201).
|
|
95
|
+
|
|
96
|
+
**Operational consequence:** a persistent intake bug is **silently retried, not dropped**, until it
|
|
97
|
+
is fixed — so a catalog/intake fix **auto-recovers the whole backlog on the next scheduled run**
|
|
98
|
+
with no manual back-fill. This is the key operational fact for the next incident: fix the intake
|
|
99
|
+
cause and let the feed re-send.
|
|
72
100
|
|
|
73
101
|
## Root cause of the intake 500s (TRUE-79534 catalog never deployed)
|
|
74
102
|
|
|
@@ -86,10 +114,12 @@ The full failure chain behind the HTTP 500s:
|
|
|
86
114
|
ongoing daily, until the catalog was loaded (2026-07-28).
|
|
87
115
|
|
|
88
116
|
The empty-`$itemId` is **unguarded** (see the gotcha): a missing code should fail cleanly with
|
|
89
|
-
"Missing AIG item ID," not a raw SQL syntax error. This defect
|
|
90
|
-
not theoretical
|
|
91
|
-
|
|
92
|
-
[
|
|
117
|
+
"Missing AIG item ID," not a raw SQL syntax error. This defect was **confirmed firing in prod**,
|
|
118
|
+
not theoretical, and the **unguarded lookup remains open** (a future missing code will 500 again).
|
|
119
|
+
Loading the catalog fixed intake, and because the feed re-sends outstanding contracts (see
|
|
120
|
+
[scheduled re-send batch](#feed-is-a-scheduled-re-send-batch-not-one-shot--self-healing)), the
|
|
121
|
+
**backlog of dropped contracts self-healed on the next scheduled run** — no manual back-fill was
|
|
122
|
+
needed (see [contract reconciliation](contract-reconciliation.md)).
|
|
93
123
|
|
|
94
124
|
## Key files / entry points
|
|
95
125
|
|
|
@@ -245,6 +275,16 @@ this interceptor or use this dual-purpose Items pattern.
|
|
|
245
275
|
|
|
246
276
|
## Change history
|
|
247
277
|
|
|
278
|
+
- 2026-07-29 — TRUE-80179 / TRUE-79534 (**RESOLVED + correction**): the `STS_001` intake gap is
|
|
279
|
+
**closed and it self-healed.** After the catalog load + collation fix shipped, AIG's next
|
|
280
|
+
scheduled batch delivered all **156** outstanding `STS_001` contracts and every one succeeded
|
|
281
|
+
(`POST /v2/entitlements` = 201 × 156 on 2026-07-29, zero failures; `Client_Aig.Entitlements`
|
|
282
|
+
+156 rows). **No manual replay needed.** Also corrected the feed model: it is a **scheduled batch
|
|
283
|
+
that re-sends OUTSTANDING/unacknowledged contracts every run** (the ~156 re-POSTs on the missing
|
|
284
|
+
catalog produced ~110,504 failed attempts for only ~100 distinct contracts), so a persistent
|
|
285
|
+
intake bug is silently retried until fixed and then **auto-recovers on the next run** — supersedes
|
|
286
|
+
any earlier "no retry" implication. The unguarded empty-`$itemId` lookup and the skipped-migration
|
|
287
|
+
deploy gap remain open. (mhammontree)
|
|
248
288
|
- 2026-07-28 — TRUE-80179 / TRUE-79534 (**CORRECTION — supersedes the entry below**): AIG intake
|
|
249
289
|
**is** a live production API feed, and it was **silently failing with HTTP 500.** The earlier
|
|
250
290
|
same-day "no live feed / bulk load only" conclusion was **wrong** — it audited `Logs_Aig` alone,
|
package/package.json
CHANGED