toga-ai 1.0.757 → 1.0.758

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: Worker
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-02
9
+ updated: 2026-09-03
10
10
  owners: ["dfranks", "bala", "jcardinal", "mhammontree", "snaredla"]
11
11
  files:
12
12
  - worker/crons/toga2/netsuite/common_sync_togasupply.php
@@ -88,9 +88,27 @@ The single most expensive misreading of this engine. `startModeIteration()` writ
88
88
  `<seconds>-RUNNING` at the **start** of every section and `finishModeIteration()` writes `-IDLE` at
89
89
  the end; the ÷3 `REDUCE_INTERVAL_FACTOR` shrink fires only because the *previous* run never reached
90
90
  its IDLE write. **There is no timeout, memory cap, or resource limit anywhere in the script** — so
91
- "didn't finish" can only mean **it threw**. A collapsing window therefore means *go read Sentry*,
92
- never *tune the window size down*. (Corollary: a window that has grown back to the cap is positive
93
- evidence the section is completing.)
91
+ "didn't finish" can only mean **it threw**. A collapsing window therefore means *go read the error
92
+ in prod* (see below), never *tune the window size down*. (Corollary: a window that has grown back to
93
+ the cap is positive evidence the section is completing.)
94
+
95
+ **Where the error is: prod `Logs.Issue` / `Logs.Event`, NOT Sentry.** Read worker errors from the
96
+ production log DB by default — you do not need a developer to point you there. The base `Logs`
97
+ schema (prod-logs cluster) holds `Issue` (deduped, with `errorMessage` + `trace`) and `Event` (each
98
+ occurrence, with `clientId`, `errorMessage`, `trace`, and a `context` JSON carrying `errorFile` +
99
+ `errorLine`). Find a sync crash like this (NYCHH `clientId = 3`):
100
+
101
+ ```sql
102
+ SELECT e.dtCreated, e.issueId, e.errorMessage, e.context
103
+ FROM Event e
104
+ WHERE e.clientId = 3 AND e.dtCreated > NOW() - INTERVAL 2 DAY
105
+ ORDER BY e.dtCreated DESC;
106
+ ```
107
+
108
+ Filter on the `context.REFERENCE.areaPath` = `crons/toga2/netsuite`. **The aggregated `Issue.trace`
109
+ can show an OLD line; trust the newest `Event.context.errorLine` for the real spot.** `context` also
110
+ gives `memoryPeakMb` — a small peak (e.g. 20 MB) rules out "giant record / OOM" and points at a
111
+ plain thrown warning instead.
94
112
 
95
113
  ### Per-record resume cursor — checkpoint after EACH record, not window-end (LIVE 2026-09-02)
96
114
 
@@ -151,6 +169,39 @@ back in the account **display** format (here `M/D/YYYY`, date-only). Proven: `SE
151
169
  but the same trap if either ever becomes one.
152
170
  - Deployed 2026-09-02 (worker + library re-deploy; both files had changed).
153
171
 
172
+ ### ⚠ The by-reference refetch dropped `lastModifiedDate` → ITEM_FULFILLMENTS + INVENTORY_ADJUSTMENTS froze (found 2026-09-03, NYCHH prod)
173
+
174
+ The per-record cursor introduced a trap specific to the two sections whose `sync*FromNetsuite()`
175
+ **refetches the record by reference**. `syncItemFulfillmentFromNetsuite(&$nsItemFulfillment, …)`
176
+ (`library/app/api/toga2.php:4535`) and `syncInventoryAdjustmentFromNetsuite(&$nsInventoryAdjustment,
177
+ …)` (`:5655`) each **replace the caller's object** with a fetch-by-id shim
178
+ (`fetchItemFulfillmentById` / `fetchInventoryAdjustmentById`). Those two fetch shims **did not set
179
+ `->lastModifiedDate`**. So the per-record cursor PUT right after the call
180
+ (`common_sync_togasupply.php:1235` fulfillments, `:1323` inventory adjustments) read
181
+ `$record->lastModifiedDate` on the **replaced** object → *Undefined property: stdClass::$lastModifiedDate*
182
+ → 1.0 `App_Error::handleError` → `exit` → the section died **before** its cursor PUT and
183
+ `finishModeIteration()`, freezing it at RUNNING with the watermark stuck years back. Confirmed on
184
+ NYCHH prod (`Logs.Issue` 649, `clientId 3`): fulfillments frozen ~2023-01-23, inventory adjustments
185
+ ~2022-12-29, both windows shrinking. `memoryPeakMb` was 20 — a fast thrown warning, **not** a
186
+ giant-record/OOM. **GroWrk was the reported alert** (ITEM_FULFILLMENTS re-throwing every 5 min
187
+ since the 2026-09-02 per-record cursor deploy). Because the fix is in **shared library code**, all
188
+ four active togasupply clients that hit these sections were affected: **GroWrk, NYCHH,
189
+ Prudential-EDI, S&P Global**.
190
+
191
+ - **ITEM_RECEIPTS was never affected**: `fetchItemReceiptById` already set `lastModifiedDate`, so its
192
+ cursor PUT succeeded (a real `…|<internalId>` value) while the other two carried the migration's
193
+ `…|0` and never moved.
194
+ - **The `|0` is NOT the bug.** It is the intended empty-window marker (see the per-record cursor
195
+ section). The frozen sections showed `|0` only because they never completed a record since the
196
+ 2026-09-02 rename.
197
+ - **Fix (library only, `php -l` clean):** set `$shim->lastModifiedDate` in **both**
198
+ `fetchItemFulfillmentById` (from the REST header's `lastModifiedDate`) and
199
+ `fetchInventoryAdjustmentById` (add `lastmodifieddate` to its SuiteQL SELECT, then set the shim).
200
+ No worker change, no SQL, fail-loud preserved. After deploy, the two sections resume and backfill
201
+ the gap on their own (idempotent) — rewinding the cursor is optional to speed catch-up.
202
+ - **Lesson:** any `sync*FromNetsuite()` that takes the record `&$byReference` and refetches must have
203
+ its fetch-by-id shim carry **every** field the caller reads afterward — `lastModifiedDate` first.
204
+
154
205
  ### ⚠ Per-record isolation is SECTION-SPECIFIC — and SALES_ORDERS has NONE (corrected 2026-08-17)
155
206
 
156
207
  Whether a bad record is "stepped over" or freezes the section depends entirely on whether **that
@@ -1124,6 +1175,22 @@ the code deploy).
1124
1175
 
1125
1176
  ## Change history
1126
1177
 
1178
+ - 2026-09-03 — **By-reference refetch dropped `lastModifiedDate` → ITEM_FULFILLMENTS + INVENTORY_ADJUSTMENTS
1179
+ froze (fixed, `library` only).** GroWrk's ITEM_FULFILLMENTS was re-throwing every 5 min since the
1180
+ 2026-09-02 per-record cursor deploy (`Undefined property: stdClass::$lastModifiedDate` at
1181
+ `common_sync_togasupply.php:1235`). Root cause: `syncItemFulfillmentFromNetsuite(&$rec)` and
1182
+ `syncInventoryAdjustmentFromNetsuite(&$rec)` **replace** the by-ref record with a fetch-by-id shim
1183
+ (`fetchItemFulfillmentById` / `fetchInventoryAdjustmentById` in `rest.php`), and neither shim set
1184
+ `->lastModifiedDate` — so the per-record cursor PUT that reads it after the call threw → `App_Error`
1185
+ `exit()` → section died before `finishModeIteration()` → frozen RUNNING, window ÷3. `fetchItemReceiptById`
1186
+ already set it, so ITEM_RECEIPTS was never hit; INVENTORY_ADJUSTMENTS was latent (no client had processed
1187
+ one since the rename). Shared code → all four active togasupply clients affected (GroWrk, NYCHH,
1188
+ Prudential-EDI, S&P Global); no data lost (the record write commits each run), self-heals after deploy.
1189
+ Fix: set `$shim->lastModifiedDate` in both shims (add `lastmodifieddate` to InventoryAdjustment's SuiteQL
1190
+ SELECT). `php -l` clean; no worker/SQL change. Lesson: any `sync*FromNetsuite()` that refetches its
1191
+ `&$byReference` record must have its fetch-by-id shim carry every field the caller reads afterward. Full
1192
+ detail in *How it works* above; shim-side entry on the
1193
+ [SuiteQL/REST shim doc](../../library/features/netsuite-suiteql-rest-shim.md). (jcardinal)
1127
1194
  - 2026-09-02 — **Per-record cursor was storing a date-ONLY, US-format value — FIXED + DEPLOYED (shared:
1128
1195
  `worker` + `library`).** The datetime half was `M/D/YYYY` with no time (e.g. `9/2/2026|6302323`), so the
1129
1196
  cursor sat at midnight and the same-second id tiebreak never matched — every run re-fetched the whole day.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.757",
3
+ "version": "1.0.758",
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",