toga-ai 1.0.752 → 1.0.753

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.
@@ -418,9 +418,14 @@ enable flags** and an optional `$monitorTogadeskDepartmentIds[]`:
418
418
  still had a RESTRICT child (item receipt `ItemReceiptItems.purchaseOrderItemId`, or an SO/TO link).
419
419
  Fixed to **unlink first, then delete**: delete the four PO bridge routes, unhook item receipts with
420
420
  `PUT /item-receipt-items/{uuid}` `['purchaseOrderItem' => null]`, then DELETE the PO line kept
421
- **throwing** as the fail-loud backstop — mirroring the SALES_ORDERS cleanup. Written + reviewed,
422
- **not yet committed/deployed**; carries a schema deploy dependency (nullable
423
- `ItemReceiptItems.purchaseOrderItemId`). Full detail, the mirror-NetSuite decision, and the api2
421
+ **throwing** as the fail-loud backstop — mirroring the SALES_ORDERS cleanup. **Now committed to
422
+ `_production` and confirmed live on Elite** (carried a schema deploy dependency: nullable
423
+ `ItemReceiptItems.purchaseOrderItemId`). **Follow-up fix same day:** the bridge-clear step's **GET**
424
+ was also a throwing `send()` — a client without an ACL grant on the transfer-order bridge routes
425
+ (Elite, Core records 329/330) got **403 EZ-2** and re-froze; the bridge/link GET loop now passes
426
+ `$throwExceptionOnApiError=false` and **skips** a link table on a no-access response (403 OR EZ-2),
427
+ while any other failure still throws. (Ops note: merging `library` to `_production` does not deploy
428
+ the worker boxes — a worker deploy does.) Full detail, the mirror-NetSuite decision, and the api2
424
429
  bridge-route-key mechanic (kebab→camel, no pluralization) live on
425
430
  [NetSuite → TOGa Supply per-client sync](../../worker/features/netsuite-togasupply-per-client-sync.md)
426
431
  (2026-09-02). (jcardinal)
@@ -649,8 +649,8 @@ the code deploy).
649
649
  — for the throwing order.
650
650
 
651
651
  - **⚠ The PURCHASE_ORDERS cleanup DELETE froze the whole section when NetSuite drops a PO line that
652
- still has an item receipt or SO/TO link (fixed 2026-09-02, `library` only — WRITTEN + REVIEWED,
653
- NOT yet committed/deployed).** Same freeze class as the SALES_ORDERS renumber-DELETE above, on a
652
+ still has an item receipt or SO/TO link (fixed + DEPLOYED 2026-09-02, `library` only — committed to
653
+ `_production`, confirmed live on Elite).** Same freeze class as the SALES_ORDERS renumber-DELETE above, on a
654
654
  different path. `syncPurchaseOrderFromNetsuite`'s cleanup loop ("remove remaining items if
655
655
  updating", `library/app/api/toga2.php`) deleted a leftover Toga PO line with a **bare, throwing**
656
656
  `send()` DELETE `/purchase-order-items/{uuid}`. When the dropped line still had an item receipt
@@ -681,6 +681,26 @@ the code deploy).
681
681
  `dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` (section 7, guarded/re-runnable).
682
682
  **Confirm that migration has run on every client DB before deploying the code fix**, or the PUT
683
683
  `purchaseOrderItem = null` fails.
684
+ - **⚠ Follow-up (deployed same day): the bridge-CLEAR GET must TOLERATE a 403 for a client that
685
+ lacks the bridge (transfer-order routes).** After the first fix deployed, Elite hit a NEW freeze.
686
+ The cleanup **GETs** each of the four bridge routes to find rows to remove, and that GET was a
687
+ **throwing** `send()`. Elite has **no ACL grant** on the transfer-order bridge Core records (**329**
688
+ `purchase-order-items-transfer-order-items`, **330** `transfer-order-items-purchase-order-items`)
689
+ because Elite does not use transfer orders, so `GET /purchase-order-items-transfer-order-items`
690
+ returned **403 EZ-2** ("no permission to read the specified fields", roleIds [1,3]) → the whole
691
+ section aborted, watermark still frozen at 2025-10-13. **Fix:** the bridge/link GET loop now calls
692
+ `App_Api_Toga2::send(..., $throwExceptionOnApiError = false)` and, on a **no-access** response
693
+ (status **403** OR error code **EZ-2**), **skips that link table** — no access means the client
694
+ cannot have rows there, so there is nothing to clear. **Any other failure still throws** (fail
695
+ loud). Covers both transfer-order bridge routes. Confirmed: Elite's PO import unfroze after
696
+ redeploy.
697
+ - **Confirming the new cleanup code is actually live (from `Logs_<Client>.Api`).** A run on the
698
+ **NEW** code shows, before the PO-line delete: `GET /purchase-order-item-sales-order-items`,
699
+ `GET /sales-order-item-purchase-order-items`, `GET /purchase-order-items-transfer-order-items`
700
+ (may be **403 and skipped**), `GET /item-receipt-items`, then `PUT /item-receipt-items/{uuid}`
701
+ (the unhook). The **OLD** code goes straight from the purchase-order PUT to a bare
702
+ `DELETE /purchase-order-items/{uuid}` with none of those. This call pattern is the ground truth
703
+ for whether the worker box is running the fix.
684
704
  - **api2 route mechanics (verified against `api2/Component/Api/V2/V2.php`).** For these list/bridge
685
705
  routes the response collection key is a **plain kebab→camel conversion of the route, no
686
706
  pluralization** (`/purchase-order-item-sales-order-items` → `purchaseOrderItemSalesOrderItems`).
@@ -695,6 +715,14 @@ the code deploy).
695
715
  "move the receipt to the surviving line" is **unsafe** — double-count / `UNIQUE(itemReceiptId,
696
716
  purchaseOrderItemId)` collision. Only clean cases self-heal; tangled ones must throw for a human.
697
717
 
718
+ - **⚠ Merging `library` to `_production` on GitHub does NOT update the running worker EC2 boxes — a
719
+ worker DEPLOY does.** After a `library` merge to `_production`, the worker instances keep running the
720
+ **OLD** `library` code across cron cycles until an actual worker deploy pushes it onto the boxes. On
721
+ 2026-09-02 the PO-cleanup fix merged at 14:48 but the worker ran old code for multiple 5-min cron
722
+ cycles (the api2 log still showed the old bare DELETE, none of the new calls) until it was deployed.
723
+ **Before concluding a `library` fix "didn't work," confirm the worker box is actually running it** —
724
+ the `Logs_<Client>.Api` call pattern is the ground truth (see the PO-cleanup call-pattern note above).
725
+
698
726
  - **⚠ Inventory-adjustment sync: two shared-engine bugs + it is still SOAP (2026-08-28).** Both fixed
699
727
  in `syncInventoryAdjustmentFromNetsuite` (`library/app/api/toga2.php`) and reachable by any client
700
728
  running inventory adjustments: (a) the stale-item prune deleted an `InventoryAdjustmentItem` before
@@ -1100,21 +1128,30 @@ the code deploy).
1100
1128
  1048 "quantityAdjustBy cannot be null" on NetSuite serialized-detail sub-lines. The null-skip reached prod
1101
1129
  ~2026-09-01 afternoon; Issue 542 stopped 09-01 14:08. Recorded that `3-IDLE`-crawl = the same poison-record
1102
1130
  shrink as `1-RUNNING`, not a slow section. (jcardinal)
1103
- - 2026-09-02 — **PURCHASE_ORDERS cleanup DELETE freeze fixed (shared, `library` only — WRITTEN +
1104
- REVIEWED, NOT yet committed/deployed).** `syncPurchaseOrderFromNetsuite`'s "remove dropped items"
1105
- cleanup deleted a NetSuite-dropped PO line with a bare throwing DELETE; a leftover item receipt
1106
- (`ItemReceiptItems.purchaseOrderItemId`, RESTRICT) or SO/TO link → 1451 → EV-11 → threw → froze the
1107
- whole PURCHASE_ORDERS section at `1-RUNNING` (Elite watermark stuck 2025-10-13, window ÷3 to 1s,
1108
- poison PO re-tried every 5 min). Fix mirrors the SALES_ORDERS cleanup: unlink all four PO bridge
1109
- routes, unhook item receipts via `PUT /item-receipt-items/{uuid}` `purchaseOrderItem = null`, then
1110
- DELETE the PO line kept **throwing** as the fail-loud backstop. **Decision:** the sync mirrors
1111
- NetSuite — remove our line + its links but keep the real records on the other side; never
1112
- log-and-skip (a log-and-skip guard was tried this session and rejected as data damage). **Deploy
1113
- dependency:** `ItemReceiptItems.purchaseOrderItemId` must be nullable
1114
- (`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` §7) on every client DB first.
1115
- Recorded the api2 bridge-route key mechanic (plain kebab→camel, no pluralization; PUT writes SQL
1116
- NULL) and Elite's 78 duplicate PO line-groups (4 with receipts, all tangled → must fail loud).
1117
- No prod data was modified (read-only SELECTs + NetSuite reads). (jcardinal)
1131
+ - 2026-09-02 — **PURCHASE_ORDERS cleanup DELETE freeze fixed + DEPLOYED, plus a follow-up 403 fix
1132
+ (shared, `library` only — committed to `_production`, confirmed live on Elite).**
1133
+ `syncPurchaseOrderFromNetsuite`'s "remove dropped items" cleanup deleted a NetSuite-dropped PO line
1134
+ with a bare throwing DELETE; a leftover item receipt (`ItemReceiptItems.purchaseOrderItemId`,
1135
+ RESTRICT) or SO/TO link → 1451 → EV-11 → threw → froze the whole PURCHASE_ORDERS section at
1136
+ `1-RUNNING` (Elite watermark stuck 2025-10-13, window ÷3 to 1s, poison PO re-tried every 5 min). Fix
1137
+ mirrors the SALES_ORDERS cleanup: unlink all four PO bridge routes, unhook item receipts via
1138
+ `PUT /item-receipt-items/{uuid}` `purchaseOrderItem = null`, then DELETE the PO line kept **throwing**
1139
+ as the fail-loud backstop. **Decision:** the sync mirrors NetSuite — remove our line + its links but
1140
+ keep the real records on the other side; never log-and-skip (a log-and-skip guard was tried this
1141
+ session and rejected as data damage). **Deploy dependency:** `ItemReceiptItems.purchaseOrderItemId`
1142
+ must be nullable (`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` §7) on every client
1143
+ DB first. Recorded the api2 bridge-route key mechanic (plain kebab→camel, no pluralization; PUT writes
1144
+ SQL NULL) and Elite's 78 duplicate PO line-groups (4 with receipts, all tangled → must fail loud).
1145
+ **Follow-up after deploy:** the cleanup's **bridge-clear GET** was itself a throwing `send()`, and a
1146
+ client without an ACL grant on the transfer-order bridge routes (Elite — no transfer orders — Core
1147
+ records 329/330) got **403 EZ-2** on `GET /purchase-order-items-transfer-order-items` and re-froze;
1148
+ the bridge/link GET loop now passes `$throwExceptionOnApiError=false` and **skips** a link table on a
1149
+ no-access response (403 OR EZ-2), while any other failure still throws. Elite PO import unfroze after
1150
+ redeploy. **Two ops lessons:** (1) merging `library` to `_production` does NOT deploy the worker EC2
1151
+ boxes — only a worker deploy does (old code ran for cron cycles after the 14:48 merge); (2) the
1152
+ `Logs_<Client>.Api` call pattern (bridge GETs + item-receipt unhook PUT vs. a bare PO-line DELETE) is
1153
+ the ground truth for whether the fix is live. No prod data was modified (read-only SELECTs + NetSuite
1154
+ reads). (jcardinal)
1118
1155
  - 2026-09-01 — **Customer-PO `dateOrder`/`cost` fixes + NYCHH inventory-adjustment serial-case unfreeze
1119
1156
  (shared, `library` only, working-tree — not yet committed/deployed).** `syncSalesOrderFromNetsuite`
1120
1157
  now sets the customer PO's `dateOrder` from the SO `tranDate` (was always NULL) and its PO lines'
@@ -126,8 +126,8 @@ the trait declares must exist as a column, a `CustomRecordFields` row **and** an
126
126
  ## Change history
127
127
 
128
128
  - 2026-09-02 — **Elite PURCHASE_ORDERS froze on a dropped PO line that still had a receipt** (shared
129
- fix, `library` only — WRITTEN + REVIEWED, NOT yet committed/deployed). Elite was the confirmed prod
130
- case: `NETSUITE_EXECUTION_MODE_PURCHASE_ORDERS='1-RUNNING'`, watermark frozen at **2025-10-13**,
129
+ fix, `library` only — now DEPLOYED to `_production`, PO import confirmed unfrozen on Elite). Elite was
130
+ the confirmed prod case: `NETSUITE_EXECUTION_MODE_PURCHASE_ORDERS='1-RUNNING'`, watermark frozen at **2025-10-13**,
131
131
  window collapsed to 1s, same poison PO re-tried every 5 min. Root cause is shared, not Elite —
132
132
  `syncPurchaseOrderFromNetsuite`'s cleanup DELETE threw on a leftover item receipt / SO link. Elite
133
133
  data behind it: **78 duplicate `(purchaseOrder, vendorItem)` PO line-groups**, only **4** carry a
@@ -138,7 +138,17 @@ the trait declares must exist as a column, a `CustomRecordFields` row **and** an
138
138
  (double-count / `UNIQUE(itemReceiptId, purchaseOrderItemId)` collision) — tangled cases fail loud
139
139
  for a human, only clean cases self-heal. **Before deploy:** confirm
140
140
  `ItemReceiptItems.purchaseOrderItemId` is nullable on `Client_Elite`
141
- (`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` §7). Full mechanics on the
141
+ (`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` §7). **Follow-up after deploy:** a
142
+ second freeze appeared — the cleanup's **bridge-clear GET** was a throwing `send()`, and Elite has
143
+ **no ACL grant** on the transfer-order bridge Core records (**329**
144
+ `purchase-order-items-transfer-order-items`, **330** `transfer-order-items-purchase-order-items`)
145
+ because Elite doesn't use transfer orders, so `GET /purchase-order-items-transfer-order-items`
146
+ returned **403 EZ-2** (roleIds [1,3]) and aborted the section. Fixed by making the bridge GET loop
147
+ tolerate a no-access response (`$throwExceptionOnApiError=false`, skip that link table on 403/EZ-2,
148
+ else throw); Elite unfroze after redeploy. Note: merging `library` to `_production` did **not**
149
+ deploy the worker boxes — an actual worker deploy was required, and the `Logs_Elite.Api` call
150
+ pattern (bridge GETs + item-receipt unhook PUT vs. a bare PO-line DELETE) is how you confirm the fix
151
+ is live. Full mechanics on the
142
152
  [engine doc](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md). (jcardinal)
143
153
  - 2026-09-01 — **Drop-ship / special-order POs were never importing for Elite** (e.g. PO tranid
144
154
  128238). Root cause was in shared code, not Elite: `listPurchaseOrders()` filtered the PO header by
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.752",
3
+ "version": "1.0.753",
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",