toga-ai 1.0.1187 → 1.0.1189

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: TOGa Commerce
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-10-23
9
+ updated: 2026-10-29
10
10
  owners: ["bala", "apeterson", "tcox"]
11
11
  files:
12
12
  - toga2-commerce/src/pages/OrderDetails/hooks/useSubmitOrder.ts
@@ -41,9 +41,9 @@ Submitting an order from the cart fires **two independent sync routines** — on
41
41
 
42
42
  `updateSalesOrderEmails()` runs **first** inside `syncSalesOrderData()`, so it is the earliest header request on the wire and the one that collides with the delete burst.
43
43
 
44
- ### ⚠ Lines are NEVER sent nested on the order write — they go one request at a time
44
+ ### ⚠ On an edit, lines are NEVER sent nested on the order write — they go one request at a time
45
45
 
46
- This is the single most misread thing about this path. **Commerce does not post an order with its `salesOrderItems` inside it.**
46
+ This is the single most misread thing about this path. **On `editOrder`, Commerce does not send the order with its `salesOrderItems` inside it.** New placement (`viewType=cart`) is the opposite: `createNewSalesOrderSubmit.ts` sends ONE `POST /v2/sales-orders` with `salesOrderItems` nested (`depth: -1`), no item sync.
47
47
 
48
48
  - `syncSalesOrdersDataFromLocalStorageCartToApi.ts` **line 20 `delete`s `salesOrderItems`** from the edit `PUT` payload outright.
49
49
  - `syncSalesOrderItemsFromLocalStorageCartToApi.ts` then writes lines **individually**:
@@ -100,6 +100,7 @@ Three changes, in `toga2-commerce`:
100
100
  - **There is NO success toaster on order placement — by design, not a bug.** `useSubmitOrder` calls `addToaster` **only in its `catch` block** (`status: 'error'`). Both success paths navigate away before anything could render: cart → `createNewSalesOrderSubmit()` → `navigate("/order-details?uuid=…&type=orderPlaced")` (no `addToaster` anywhere in it); edit-order → `window.location.href = getSupplyRedirectLink(orderUuid)`. **Consequence for debugging: placing an order is not a valid way to test toaster styling.** Use a path that stays mounted — add/edit shipping address (Cart or Account), Account → My Settings, or Cart → add new user; for the error variant, force a submit failure to hit the catch block. See [blox Toaster](../../toga-blox/features/toaster.md).
101
101
 
102
102
  ## Change history
103
+ - 2026-10-29 — Scoped "never nested" to the edit path; new placement nests lines in one `POST /v2/sales-orders` (TRUE-83381). (bala)
103
104
  - 2026-10-23 — Price check before both submit paths (TRUE-82729, pending merge); see [cart price refresh](./cart-price-refresh.md). (tcox)
104
105
  - 2026-08-11 — Item sync before header sync, `depth: 1`, self-overwrite `PUT` dropped (TRUE-80551). (bala, apeterson)
105
106
 
@@ -23,7 +23,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
23
23
  ## 2.0 framework
24
24
 
25
25
  - **_underscore** (_Underscore) _(framework core)_ — 117 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
26
- - **worker2** (Worker) — 101 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
26
+ - **worker2** (Worker) — 102 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
27
27
  - **api2** (API) — 29 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
28
28
  - **dbchanges2** (Database Changes) _(framework core)_ — 24 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
29
29
  - **toga2-supply** (TOGa Supply) — 9 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
@@ -26,6 +26,7 @@
26
26
  | [Compass: NetSuite duplicate sales-order monitor (Monitor/Compass/NetsuiteDuplicateSalesOrders)](features/oneuptime-netsuite-duplicate-sales-orders-monitor.md) | 2.0 | worker2 OneUptime push monitor that queries NetSuite (SuiteQL) for Office Depot POs with more than one open NetSuite sales order; open when working on Compass d |
27
27
  | [Compass: Office Depot inbound AS2 files monitor (Monitor/Compass/OfficeDepotInboundFiles)](features/oneuptime-office-depot-inbound-files-monitor.md) | 2.0 | worker2 OneUptime push monitor that checks Office Depot 850/997 files are arriving over AS2 and are moved out of `agilant-as2/processed/` to the import folder; |
28
28
  | [Compass: Office Depot lost orders monitor (Monitor/Compass/OfficeDepotLostOrders)](features/oneuptime-office-depot-lost-orders-monitor.md) | 2.0 | worker2 OneUptime push monitor that alarms when cron 3a logged an Office Depot 850 in legacy `Logs.FileLog` but no ODP PurchaseOrder exists in `Client_Compass` |
29
+ | [Compass USA: order placement errors monitor (Monitor/Compass/OrderPlacementErrors)](features/oneuptime-order-placement-errors-monitor.md) | 2.0 | worker2 OneUptime push monitor that alarms when a real Compass USA user gets an API error placing an order in TOGa Commerce (`POST /v2/sales-orders` >= 400 in ` |
29
30
  | [Compass USA + Canada — order fulfillment status (standard base per-line rule)](features/order-fulfillment-status-per-line.md) | 2.0 | How Compass USA and Canada compute Pending / Picked / Packed / Partially Fulfilled / Fulfilled (the standard base per-line rule since TRUE-79669) and why the ol |
30
31
  | [People File Import admin page (tools /compass/people-file-import → worker2)](features/people-file-import-admin-page.md) | 1.0 | Browser page that lists the Compass PEOPLE files on SFTP and runs the importer on demand; open before changing the page or the importer's listing/guard code. |
31
32
  | [Compass PEOPLE-File User Lifecycle (duplicate accounts, reactivation grace window, raw-SQL deactivation)](features/people-file-user-lifecycle.md) | 2.0 | The nightly Compass PEOPLE-file cron's Users-row lifecycle (insert/reactivate/deactivate), why it creates duplicate accounts, and its raw-SQL/index traps; open |
@@ -0,0 +1,52 @@
1
+ ---
2
+ title: "Compass USA: order placement errors monitor (Monitor/Compass/OrderPlacementErrors)"
3
+ framework: "2.0"
4
+ repo: worker2
5
+ project: Worker
6
+ client: compass-usa
7
+ type: client-feature
8
+ status: active
9
+ updated: 2026-10-29
10
+ owners: ["bala"]
11
+ files:
12
+ - worker2/Worker/Monitor/Compass.php
13
+ - toga2-commerce/src/pages/OrderDetails/helpers/createNewSalesOrderSubmit.ts
14
+ related:
15
+ - ../../../2.0/apps/worker2/features/oneuptime-worker2-monitoring.md
16
+ - ../../../2.0/apps/api2/features/request-logging.md
17
+ - ../../../2.0/apps/toga2-commerce/features/order-submit-sync-sequencing.md
18
+ - ./oneuptime-office-depot-lost-orders-monitor.md
19
+ - ../profile.md
20
+ ---
21
+ worker2 OneUptime push monitor that alarms when a real Compass USA user gets an API error placing an order in TOGa Commerce (`POST /v2/sales-orders` >= 400 in `Logs_Compass.Api`); open when it alarms, when tuning it, or when a Compass user says checkout failed.
22
+
23
+ ## Summary
24
+ `Monitor/Compass/OrderPlacementErrors` (worker2 `_Worker_Monitor_Compass`, TRUE-83381). `Core.CronJobs` schedule `*/10 * * * *`, `maxExecutionTime` 120. OneUptime heartbeat monitor **"Compass USA Order Placement Errors"**: degraded after 25 min, down after 35 min with no push. Follows the shared [OneUptime push-metric pattern](../../../2.0/apps/worker2/features/oneuptime-worker2-monitoring.md); the push URL is an in-method local and a credential (not recorded here).
25
+
26
+ Why: Commerce shows the user the error and tells nobody else, so the team heard only when Compass reported it. Baseline 30 days to 2026-10-28: 1,748 good user order POSTs, 9 failed (7 by one user in 3 minutes on 2026-10-20, `EV` "error while trying to locate the references for the specified field"; 1 on 10-13; 1 on 10-05 "error while attempting to save the data model").
27
+
28
+ ## How it works
29
+ 1. Registers `Logs_Compass` (the Logs cluster, `_Config::databaseLogs()`) in-method and calls `_Database::resetQueryCache()` (same SQL every run).
30
+ 2. Reads `Logs_Compass.Api`, `dtStamp` window **11 to 1 minutes ago** (10-minute lookback = the cron interval, so no gaps; shifted 1 minute back so a still-running call is not counted as a crash). `dtStamp` is indexed and leads the filter.
31
+ 3. Filters: `method = 'POST'`, `route = '/v2/sales-orders'`, `userId IS NOT NULL` (a person, not an API key), `requestPayload LIKE` the Compass customer uuid `4e480554-0c85-11ef-8e3b-0aae2980db55` (`Client_Compass.Customers.id 2` "Compass", the storefront customer), `responseCode >= 400 OR NULL` (NULL = the call crashed).
32
+ 4. Any row → `alarm: HIGH`, else `OK`. Clears itself on the next clean window.
33
+
34
+ Payload: `status`, `alarm`, `reason`, `failedCount`, `affectedUserCount`, `failures` (max 20 lines: `dtStamp`, user name from `responsePayload.audience.user`, route, code, `messages[0].message` cut to 200 chars), `lookbackMinutes`, `checkedAtUtc`.
35
+
36
+ **No try/catch** (team choice): a DB failure throws, no push goes out, and the heartbeat goes degraded/down. A failed push only logs the HTTP code (never the URL).
37
+
38
+ Decisions (2026-10-29, with bala): every 10 minutes, 10-minute lookback; customer 2 storefront orders only; integration-key failures left out (API keys `apiId` 1/2 post standalone `/v2/sales-order-items` and have their own owners).
39
+
40
+ ## Gotchas
41
+ - **Filter by the customer uuid inside `requestPayload`, not by `SalesOrders.customerId`.** A failed order writes no `SalesOrders` row, and `Logs_*` is a different cluster from `Client_Compass` (no join). The `LIKE` is cheap only because the `dtStamp` window is 10 minutes; an unbounded route/payload scan over 30 days timed out ([request-logging query rules](../../../2.0/apps/api2/features/request-logging.md)).
42
+ - **New placement nests the lines in `POST /v2/sales-orders`** (`createNewSalesOrderSubmit.ts`, `salesOrderItems` in the body), so one route covers checkout. **Order edits are not covered:** the edit path writes lines one at a time (`POST /v2/sales-orders/{uuid}/sales-order-items`, `PUT`/`DELETE /v2/sales-order-items/{uuid}`), see [order-submit sync](../../../2.0/apps/toga2-commerce/features/order-submit-sync-sequencing.md).
43
+ - **Blind spot: a throw from a PRE interceptor leaves no `Api` row**, so that rejection never reaches this monitor ([request-logging](../../../2.0/apps/api2/features/request-logging.md)). A normal 400 is logged with the full response envelope; `audience.app` = "TOGa Commerce", `audience.user` = the user's display name.
44
+ - `userId` is the token's user: a staff "view as" session logs under the Compass user's id, so a hit may be our own staff.
45
+
46
+ ## Change history
47
+ - 2026-10-29 — Created: user `POST /v2/sales-orders` errors for customer 2, every 10 minutes (TRUE-83381). (bala)
48
+
49
+ ## Related
50
+ - [OneUptime push-metric monitors for 2.0 workers](../../../2.0/apps/worker2/features/oneuptime-worker2-monitoring.md)
51
+ - [V2 request logging](../../../2.0/apps/api2/features/request-logging.md)
52
+ - [Office Depot lost orders monitor](./oneuptime-office-depot-lost-orders-monitor.md)
@@ -22,7 +22,7 @@ project: _Underscore
22
22
  client: compass-usa
23
23
  type: profile
24
24
  status: active
25
- updated: 2026-10-28
25
+ updated: 2026-10-29
26
26
  owners: [jcardinal, bala, tcox, apeterson, dfranks, akhokhani, ajean, rgirish, amadda]
27
27
  files: []
28
28
  related:
@@ -60,6 +60,7 @@ related:
60
60
  - features/odp-edi-855-acknowledgement-and-overquantity-guard.md
61
61
  - features/odp-edi-file-retention-and-retry.md
62
62
  - features/oneuptime-office-depot-lost-orders-monitor.md
63
+ - features/oneuptime-order-placement-errors-monitor.md
63
64
  - ../../2.0/apps/worker2/features/compass-vip-support-importer.md
64
65
  - ../../2.0/apps/toga2-commerce/features/expedited-shipping-gating.md
65
66
  - ../../2.0/apps/toga2-commerce/features/category-tile-sort-order.md
@@ -137,6 +138,11 @@ separate, related client (see its own profile).
137
138
  rows across 110 bundles still on the Agilant twins produced a fresh bad order on every kit
138
139
  checkout. Fixed by restoring `BundleItems` in the complete catalog-fold reversal (fix is data, not
139
140
  frontend): [catalog-fold reversal](workflows/catalog-fold-reversal.md).
141
+ - **Checkout API errors alarm in OneUptime (2026-10-29, TRUE-83381).** worker2
142
+ `Monitor/Compass/OrderPlacementErrors` checks `Logs_Compass.Api` every 10 minutes for a user
143
+ `POST /v2/sales-orders` >= 400 for customer 2 "Compass" (baseline: 9 failed vs 1,748 good in 30
144
+ days). Order edits and PRE-interceptor rejections are not covered:
145
+ [order placement errors monitor](features/oneuptime-order-placement-errors-monitor.md).
140
146
 
141
147
  ## Vendors & integrations
142
148
  - **Office Depot (ODP)** — vendor id 1. ASNs arrive via **cXML** (direct V2 API) and via the
@@ -6,10 +6,11 @@ project: Worker
6
6
  client: nychh
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-10-16
9
+ updated: 2026-10-29
10
10
  owners: ["bala", "rgirish", "jcardinal", "mhammontree"]
11
11
  files:
12
12
  - worker2/Worker/Client/Nychh/TransferOrderEmails.php
13
+ - worker2/Worker/Infrastructure/Email/Send.php
13
14
  - _underscore/Model/Nychh/TransferOrder.php
14
15
  - _underscore/Model/Nychh/ItemFulfillment.php
15
16
  - dbchanges2/Client_Nychh/2026-09-15a - TransferOrderEmails.sql
@@ -58,6 +59,8 @@ Rules that carry most of the risk:
58
59
  5. **Shipped fires on the first TRACKING NUMBER, never on the NetSuite shipped stage** (since
59
60
  2026-10-05). The warehouse often marks a fulfillment shipped only after delivery, so the stage is
60
61
  unreliable. No tracking → no Shipped email, even at stage shipped. See below.
62
+ 6. **Shipped is RE-SENT when a new tracking number lands after it** (since 2026-10-21, TRUE-82673).
63
+ Same template and subject, so the customer reads it as a duplicate. See *Shipped resend*.
61
64
 
62
65
  Recipient is `DevTeam@togatech.com` while testing. `2026-09-15b` swaps it to
63
66
  `EndUserDeviceLifecycle@nychhc.org` — run that file only after sign-off.
@@ -68,10 +71,14 @@ Recipient is `DevTeam@togatech.com` while testing. `2026-09-15b` swaps it to
68
71
  - `SendSubmitted(string $transferOrderUuid)` — queued from the TransferOrder `postPut`.
69
72
  - `SendFulfillment(string $itemFulfillmentUuid)` — queued from the ItemFulfillment hooks.
70
73
  - `SendPending(bool $dryRun = true)` — the `*/5` backstop cron.
71
- - `resolveStagesStillToAnnounce(stageName, stageAlreadyEmailed, hasTrackingNumber)` — which
72
- stages are still owed (see *Shipped = tracking number*).
74
+ - `resolveStagesStillToAnnounce(stageName, stageAlreadyEmailed, hasTrackingNumber,
75
+ hasTrackingAddedSinceEmail)` — which stages are still owed (see *Shipped = tracking number*).
73
76
  - `buildHasTrackingNumberSql()` — the one "has tracking" test, shared by
74
77
  `readItemFulfillmentForEmail()` (`hasTrackingNumber` column) and the `SendPending` sweep.
78
+ - `buildHasTrackingAddedSinceEmailSql()` — the one "tracking arrived after the last Shipped email"
79
+ test, shared the same way (`hasTrackingAddedSinceEmail` column + sweep). See *Shipped resend*.
80
+ - `readTrackingForEmail(itemFulfillmentId, isAddedSinceLastEmailOnly, addedUpToDateTime)` — all
81
+ numbers on the first Shipped, only the new ones on a resend.
75
82
  - `readLinesMissingAssetTags()` / `sendPackedExceptionEmail()` — the asset-tag hold.
76
83
  - `initialize()` registers the client DB before any query runs.
77
84
  - Builders: `buildItemRowsHtml()`, `buildTrackingSectionHtml()`, `buildAssetTagTableHtml()`,
@@ -131,7 +138,8 @@ they must stay in step: the Submitted guard, the order read, and both `SendPendi
131
138
  `c_dtTransferOrderSubmittedEmailSent` marker, sends, then stamps the marker.
132
139
  2. **Packed / Shipped** — the ItemFulfillment `postPost` / `postPut` queue `SendFulfillment` with
133
140
  the fulfillment uuid. The stage reached is recorded on `c_transferOrderEmailStageSent`, with
134
- `c_dtTransferOrderEmailSent` as the timestamp, so a fulfillment cannot re-announce a stage.
141
+ `c_dtTransferOrderEmailSent` as the timestamp, so a fulfillment cannot re-announce a stage
142
+ (one exception: the Shipped resend).
135
143
  `resolveStagesStillToAnnounce()` decides what is owed (table in *Shipped = tracking number*).
136
144
  When packed was never announced and shipped is now owed it returns **`[packed, shipped]`**, both
137
145
  sent in that order in one run — the **asset-tag check lives on packed**, so packed must never be
@@ -139,7 +147,8 @@ they must stay in step: the Submitted guard, the order read, and both `SendPendi
139
147
  sync is a **daily** cron, so a fulfillment landing fully shipped/tracked is normal — see
140
148
  [the per-client sync](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md).
141
149
  3. The **backstop cron** sweeps anything the hooks missed (below).
142
- 4. All three paths share the same markers, so nothing sends twice.
150
+ 4. All three paths share the same markers, so nothing sends twice by accident (the Shipped resend
151
+ is on purpose).
143
152
  5. The worker renders the stored `EmailTemplates` body, fills `{orderItems}` and
144
153
  `{trackingSection}` itself, and sends through `_Model_Client_EmailTemplate`.
145
154
 
@@ -173,7 +182,8 @@ PUT follows.** Fulfillment **2704** in production: header row at 20:33:09, lines
173
182
 
174
183
  | Marker (`c_transferOrderEmailStageSent`) | Tracking | Stage | Owed |
175
184
  |---|---|---|---|
176
- | `shipped` | any | any | nothing |
185
+ | `shipped` | added since last email | any | `[shipped]` again (resend) |
186
+ | `shipped` | otherwise | any | nothing |
177
187
  | `packed` | yes | any | `[shipped]` |
178
188
  | empty | yes | any (incl. picked — tracking means boxed) | `[packed, shipped]` |
179
189
  | empty | no | packed / shipped | `[packed]` |
@@ -181,16 +191,58 @@ PUT follows.** Fulfillment **2704** in production: header row at 20:33:09, lines
181
191
 
182
192
  The asset-tag hold runs whenever packed is owed and blocks both.
183
193
 
184
- - **⚠ `SendPending` WHERE must match that table:** marker NULL or `<> 'shipped'` AND (has tracking OR
185
- (stage IN packed/shipped AND `COALESCE(marker,'') = ''`)). Drift → rows picked then skipped, eating
186
- `LIMIT 25`.
194
+ - **⚠ `SendPending` WHERE must match that table:** (marker NULL or `<> 'shipped'` AND (has tracking OR
195
+ (stage IN packed/shipped AND `COALESCE(marker,'') = ''`))) OR (marker = `shipped` AND
196
+ `buildHasTrackingAddedSinceEmailSql()`). Drift → rows picked then skipped, eating `LIMIT 25`.
187
197
  - **Why the cron, not the hook, sends Shipped:** library `syncItemFulfillmentFromNetsuite`
188
198
  (`library/app/api/toga2.php`) PUTs the stage first (~L7270), then units/asset tags, and writes
189
199
  package tracking **last** (~L8046). The ItemFulfillment `postPut` job runs before tracking exists.
190
200
  (Also removes the old race where Shipped read stale tracking/tags.) Tracking imports at picked,
191
201
  packed or shipped alike.
192
- - **Once per fulfillment.** Business rule = 1 tracking number per fulfillment; prod has violations
193
- (IF 329047 has 2; IFs 329944/330013 share 1). Still sends once — the marker gates it.
202
+ - **Once per fulfillment, plus one resend per NEW tracking number.** Business rule = 1 tracking number
203
+ per fulfillment; prod has violations (IF 329047 has 2; IFs 329944/330013 share 1). Numbers present at
204
+ the first Shipped all go in that one email. A number that arrives later re-sends Shipped — see
205
+ *Shipped resend*.
206
+
207
+ ### Shipped resend when tracking changes (TRUE-82673, live 2026-10-21)
208
+
209
+ worker2 `c4d5008` + `1a1e3a0`, PR #193. Built for a warehouse re-keying a mistyped number.
210
+
211
+ - **Trigger** = `buildHasTrackingAddedSinceEmailSql()`: a linked `TrackingNumbers` row with
212
+ `dtCreated > ItemFulfillments.c_dtTransferOrderEmailSent`. The sync POSTs a new `TrackingNumbers`
213
+ row for a number the client has never used, so `dtCreated` = arrival. A number the client already
214
+ holds is reused with its OLD `dtCreated`, so re-attaching an existing number is NOT announced.
215
+ - **Cutoff, not NOW().** `SendFulfillment()` reads DB time first (`$trackingCutoff`), uses it as the
216
+ upper bound for the tracking read, and stores it as `c_dtTransferOrderEmailSent`. A number landing
217
+ mid-send is still newer than the marker, so the next run announces it.
218
+ - **Body lists only the new number(s)** (`isAddedSinceLastEmailOnly`). The old number stays linked in
219
+ `ItemFulfillments_TrackingNumbers` (the sync needs it) but is left out of the email.
220
+ - **⚠ Same template, same subject ("Order Shipped: N items"), same wording.** Nothing says it is a
221
+ correction, so the customer reports it as a duplicate. Expected behavior, not a bug.
222
+
223
+ ### Triage: customer reports a duplicate Shipped email
224
+
225
+ Read-only prod, in order:
226
+
227
+ 1. `Client_Nychh.TransferOrders` by `number` LEFT JOIN `ItemFulfillments` ON `transferOrderId` →
228
+ `c_transferOrderEmailStageSent`, `c_dtTransferOrderEmailSent` per fulfillment.
229
+ 2. `Logs_Nychh.Email WHERE INSTR(body, '<TO number>') > 0` → every email for the order, with `to`,
230
+ `cc`, `bcc`, `dtSent` (columns are `to`/`cc`/`bcc`, not `toAddresses`).
231
+ 3. `ItemFulfillments_TrackingNumbers` JOIN `TrackingNumbers` for the IF → compare each
232
+ `TrackingNumbers.dtCreated` with `c_dtTransferOrderEmailSent`. A later number = the resend.
233
+ 4. Two copies of the SAME email (same number, same minute): `Core.WorkerJobs WHERE
234
+ action = 'Infrastructure/Email/Send/Run'` around `dtSent` (cron `Core.CronJobs` id 37). One run =
235
+ we sent it once; the second copy came from the client side.
236
+
237
+ - **toga-db MCP rejects `REPLACE()`** as a forbidden keyword. Use `INSTR(body,'x') > 0`.
238
+ - **Real case, TRUE-83389 (2026-10-29):** TO 294663 (`TransferOrders.id` 2071), one IF 333376
239
+ (`ItemFulfillments.id` 3104). Shipped #1 = Email 198, 2026-10-26 17:26, tracking `30494276`
240
+ (TrackingNumbers 4059). NetSuite re-keyed it to `34094276` (TrackingNumbers 4063, created
241
+ 2026-10-29 08:36, carrier 101 New York Minute) → Shipped #2 = Email 204, 08:41, only the new
242
+ number. Both bridges (4211, 4215) stay linked. The "04"→"40" swap looks like a typo fix (inferred).
243
+ The client's two screenshots were both Email 204, sent in one Email Send run at 08:41:45, so the
244
+ doubled copy came from their side (To `EndUserDeviceLifecycle@nychhc.org` + CC
245
+ `HealthcareteamNYCHHC@togatech.com` reaching one person is a guess, not proven).
194
246
  - **Lag:** adding tracking in NetSuite bumps the IF `lastmodifieddate`, so the sync re-reads it, but
195
247
  only on the NYCHH sync interval (daily). E.g. IF 330377 (NS 7566823) still Packed (status B) in
196
248
  NetSuite, tracking imported 2026-10-05 11:56.
@@ -417,6 +469,9 @@ loaded out of the dbchanges2 file so an unresolved `{placeholder}` fails a test.
417
469
  unescaped values and both detail-box defects were all found there, not in production.
418
470
 
419
471
  ## Change history
472
+ - 2026-10-29 — Recorded the Shipped resend on a new tracking number (TRUE-82673, live 2026-10-21) and
473
+ the duplicate-email triage steps, from TRUE-83389 (TO 294663). Investigation only, no code change.
474
+ (bala)
420
475
  - 2026-10-16 — Linked the emails-owed monitor (TRUE-82758). (mhammontree)
421
476
  - 2026-10-09 — **New York Minute shows its logo, and NON_SERIALIZED lines are ignored by the asset-tag
422
477
  reads** (TRUE-82527). `CARRIER_LOGO_URL_BY_CARRIER_NAME` gained `NEW YORK MINUTE` (no tracking site,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.1187",
3
+ "version": "1.0.1189",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",