toga-ai 1.0.751 → 1.0.752

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.
@@ -104,18 +104,22 @@ that worker job does the syncing, this library class notices when it stops.
104
104
  checkpoints mean anything; and a client that is held / never transacts supply (AIG) must be
105
105
  left **off** this list or it produces permanent false-positive tickets.
106
106
 
107
- - **⚠ The pending cursor-rename in the sync will SILENTLY BLIND this monitor (BUILT 2026-09-02, not yet
108
- deployed).** The per-record resume cursor work on the sync renames each section's position key from
109
- `NETSUITE_LAST_SYNC_DATETIME_<SECTION>` to `NETSUITE_LAST_SYNC_CURSOR_<SECTION>` (value becomes
110
- `"<lastmodifieddate>|<netsuite id>"`). This monitor's `NETSUITE_SYNC_PARAMETER_KEYS` still lists the old
111
- `DATETIME` keys, so after the rename its `where..in` `/parameters` read matches **zero rows** and it
112
- reports OK forever — no stale-checkpoint alerts. The sync developer **intentionally left the monitor
113
- un-updated** this session (accepted gap). When the rename deploys, update `NETSUITE_SYNC_PARAMETER_KEYS`
114
- to the `CURSOR` names **and** parse the `<date>|<id>` value (`strtotime` the part before `|`) before the
115
- 48h compare. See the [per-client sync doc](../../worker/features/netsuite-togasupply-per-client-sync.md).
107
+ - **⚠ THIS MONITOR IS NOW BLIND — the cursor-rename shipped 2026-09-02 and this monitor was NOT updated.**
108
+ The sync's per-record resume cursor deployed to prod 2026-09-02 (worker `06e8f9ef` + library `3a70b59c`),
109
+ renaming each section's position key from `NETSUITE_LAST_SYNC_DATETIME_<SECTION>` to
110
+ `NETSUITE_LAST_SYNC_CURSOR_<SECTION>` (value now `"<lastmodifieddate>|<netsuite id>"`). This monitor's
111
+ `NETSUITE_SYNC_PARAMETER_KEYS` still lists the old `DATETIME` keys, so its `where..in` `/parameters` read
112
+ now matches **zero rows** and it reports OK forever — **no stale-checkpoint alerts fire**. This was an
113
+ accepted gap at deploy time, but it is now live. **FIX OWED:** update `NETSUITE_SYNC_PARAMETER_KEYS` to the
114
+ `CURSOR` names **and** parse the `<date>|<id>` value (`strtotime` the part before `|`) before the 48h
115
+ compare. See the [per-client sync doc](../../worker/features/netsuite-togasupply-per-client-sync.md).
116
116
 
117
117
  ## Change history
118
118
 
119
+ - 2026-09-02 — **The sync's `NETSUITE_LAST_SYNC_DATETIME_*` → `NETSUITE_LAST_SYNC_CURSOR_*` rename SHIPPED to
120
+ prod (worker `06e8f9ef` + library `3a70b59c`), so this monitor is now BLIND** — its `where..in` read matches
121
+ zero rows and it reports OK forever. Monitor was not updated with the rename (accepted gap, now live). Fix
122
+ owed: update `NETSUITE_SYNC_PARAMETER_KEYS` to the CURSOR names + `strtotime` the part before `|`. (jcardinal)
119
123
  - 2026-09-02 — Flagged that the sync's pending `NETSUITE_LAST_SYNC_DATETIME_*` →
120
124
  `NETSUITE_LAST_SYNC_CURSOR_*` key rename (BUILT, not yet deployed) will make this monitor match zero rows
121
125
  and stop alerting; it was intentionally left un-updated. Update the key list + parse the `<date>|<id>`
@@ -92,12 +92,14 @@ its IDLE write. **There is no timeout, memory cap, or resource limit anywhere in
92
92
  never *tune the window size down*. (Corollary: a window that has grown back to the cap is positive
93
93
  evidence the section is completing.)
94
94
 
95
- ### Per-record resume cursor — checkpoint after EACH record, not window-end (BUILT 2026-09-02, NOT yet deployed)
95
+ ### Per-record resume cursor — checkpoint after EACH record, not window-end (LIVE 2026-09-02)
96
96
 
97
97
  The historical weakness of window-end-only checkpointing: a mid-window crash (poison record, or the
98
98
  log-DB doom loop above) re-processes the **whole** window every run — the giant-fulfillment doom loop is a
99
99
  direct consequence. This change converts the checkpoint to **per-record keyset (seek) resume** so a
100
- transient crash resumes at the next record instead of redoing the window.
100
+ transient crash resumes at the next record instead of redoing the window. **Deployed to prod
101
+ 2026-09-02** — worker commit `06e8f9ef` + library commit `3a70b59c` (both tips of `_production`), with the
102
+ rename migration run on every client DB first (see the deploy-order gotcha below).
101
103
 
102
104
  - **Key rename per section:** `NETSUITE_LAST_SYNC_DATETIME_<SECTION>` → `NETSUITE_LAST_SYNC_CURSOR_<SECTION>`,
103
105
  value format **`"<lastmodifieddate>|<netsuite internal id>"`**. The id breaks same-second ties.
@@ -109,10 +111,16 @@ transient crash resumes at the next record instead of redoing the window.
109
111
  already carried it).
110
112
  - **Save after EACH main record** (one PUT of `lastmod|id`); on a clean window with 0 records, PUT
111
113
  `"<windowTop>|0"` so empty date ranges still advance. No new `try/catch` — stays fail-loud.
112
- - **Migration is MANDATORY with the code (see the reset trap in Gotchas):**
114
+ - **Migration is MANDATORY, and runs BEFORE the code (see the deploy-order gotcha in Gotchas):**
113
115
  `dbchanges2/Client/2026-09-02b - NetsuiteSyncCursorRename.sql` (Client fan-out) renames the keys **and**
114
- appends `|0` to carry the value across; safe to re-run. The new-client seed module
115
- (`dbchanges2/_modules/netsuite/2026-08-05 - CLEAN NETSUITE CLINET.SQL`) was updated to CURSOR.
116
+ appends `|0` to carry the value across; safe to re-run. It was run on all client DBs first, then the
117
+ code deployed — the required order.
118
+ - **⚠ OPEN follow-up — new-client seed was edited in place (dbchanges2 rule violation).** The seed module
119
+ `dbchanges2/_modules/netsuite/2026-08-05 - CLEAN NETSUITE CLINET.SQL` had its 6 seed rows **renamed in
120
+ place** to CURSOR keys with `|0`. That breaks the dbchanges2 "never edit an already-applied file" rule and
121
+ the client-schema drift audit. **Revert that file and add a NEW dated `_modules/netsuite/` file** carrying
122
+ the same guarded `UPDATE ... WHERE key LIKE 'NETSUITE_LAST_SYNC_DATETIME_%'` so future new clients land on
123
+ CURSOR keys. Still uncommitted in the working tree.
116
124
 
117
125
  ### ⚠ Per-record isolation is SECTION-SPECIFIC — and SALES_ORDERS has NONE (corrected 2026-08-17)
118
126
 
@@ -330,9 +338,11 @@ Per-client `Parameters` table (2.0 client DB, e.g. `Client_Canon.Parameters`; co
330
338
 
331
339
  Parameters are stored **per client DB** but accessed **through the TOGa2 API**, not direct SQL.
332
340
 
333
- **Pending rename (BUILT 2026-09-02, not yet deployed):** the position keys become
334
- `NETSUITE_LAST_SYNC_CURSOR_<SECTION>` with value `"<lastmodifieddate>|<netsuite internal id>"` — see the
335
- per-record resume cursor in *How it works*. The `NETSUITE_EXECUTION_MODE_*` keys are unchanged.
341
+ **Key rename (LIVE 2026-09-02):** the position keys are now
342
+ `NETSUITE_LAST_SYNC_CURSOR_<SECTION>` with value `"<lastmodifieddate>|<netsuite internal id>"` for all 6
343
+ sections — see the per-record resume cursor in *How it works*. The `NETSUITE_EXECUTION_MODE_*` keys are
344
+ unchanged. Renamed on every client DB by `Client/2026-09-02b - NetsuiteSyncCursorRename.sql` (run before
345
+ the code deploy).
336
346
 
337
347
  ## Client variations
338
348
 
@@ -488,6 +498,19 @@ per-record resume cursor in *How it works*. The `NETSUITE_EXECUTION_MODE_*` keys
488
498
  `$dateField='lastmodifieddate'` and the engine calls it without the 4th arg. So a backdated
489
499
  `trandate` is irrelevant to whether an invoice imports, and a just-modified invoice still won't
490
500
  import until it is >24h old (the upper-bound cap).
501
+ - **⚠ DEPLOY ORDER: the cursor-rename migration MUST run on EVERY client DB before the worker/library
502
+ deploy — there is NO old-key fallback in code.** `Client/2026-09-02b - NetsuiteSyncCursorRename.sql`
503
+ renames `NETSUITE_LAST_SYNC_DATETIME_<SECTION>` → `NETSUITE_LAST_SYNC_CURSOR_<SECTION>`. If the code
504
+ ships first, `startModeIteration()` reads a CURSOR key that doesn't exist yet and falls back to
505
+ `MIN_DATETIME_TO_CHECK_FOR_NETSUITE_DATA` (= `'2018-01-01'` for 17 of 18 clients) — re-scanning ~8 years
506
+ across all sections and all clients at once, a thundering herd against NetSuite + api2. Both the php and
507
+ sql reviewers flagged this as critical. The 2026-09-02 deploy followed the correct order (migration on all
508
+ client DBs first, then code). Same rule for any future position-key rename: schema first, code second.
509
+ - **Per-record checkpoint PUT multiplies api2 round-trips on high-count catch-up windows (tuning note, not
510
+ shipped).** The per-record resume cursor does one `App_Api_Toga2::send` PUT per record. On a big backfill
511
+ window (e.g. sales orders / POs) that is one extra api2 round-trip per record. Optional future tuning:
512
+ batch the checkpoint every N records while still writing it **before** the next record's dispatch (so
513
+ crash-resume stays correct). Not built.
491
514
  - **A parse error in the shared `library/app/api/netsuite/rest.php` takes down EVERY client at
492
515
  once (tier-wide outage).** All wrappers autoload `App_Api_Netsuite_Rest` in the pre-section
493
516
  LOOKUPS/setup phase of `common_sync_togasupply.php` (~line 433). Because PHP parses the whole
@@ -1044,8 +1067,16 @@ per-record resume cursor in *How it works*. The `NETSUITE_EXECUTION_MODE_*` keys
1044
1067
 
1045
1068
  ## Change history
1046
1069
 
1047
- - 2026-09-02 — **Per-record resume cursor BUILT (shared: `worker` + `library` + `dbchanges2`; pending
1048
- review + deploy, NOT shipped).** Converts the section checkpoint from window-end-only to per-record
1070
+ - 2026-09-02 — **Per-record resume cursor DEPLOYED to prod (shared: `worker` + `library` + `dbchanges2`).**
1071
+ Now live: worker commit `06e8f9ef` + library commit `3a70b59c` (tips of `_production`); rename migration
1072
+ `Client/2026-09-02b - NetsuiteSyncCursorRename.sql` run on all client DBs **before** the code. Recorded the
1073
+ deploy-order rule (no old-key fallback — code-first would fall back to `MIN_DATETIME_TO_CHECK_FOR_NETSUITE_DATA`
1074
+ = `'2018-01-01'` and thundering-herd re-scan ~8 years for all clients; php + sql reviewers flagged it
1075
+ critical). Two open items: (1) the new-client seed `_modules/netsuite/2026-08-05 - CLEAN NETSUITE CLINET.SQL`
1076
+ was edited in place (dbchanges2 rule violation) — revert + add a new dated file; (2) optional tuning — batch
1077
+ the per-record checkpoint PUT every N records on high-count windows. Both still uncommitted. (jcardinal)
1078
+ - 2026-09-02 — **Per-record resume cursor BUILT (shared: `worker` + `library` + `dbchanges2`; shipped later
1079
+ the same day — see entry above).** Converts the section checkpoint from window-end-only to per-record
1049
1080
  keyset resume so a mid-window crash (poison record or the log-DB doom loop) resumes at the next record
1050
1081
  instead of re-processing the whole window. Position key `NETSUITE_LAST_SYNC_DATETIME_<SECTION>` →
1051
1082
  `NETSUITE_LAST_SYNC_CURSOR_<SECTION>`, value `"<lastmodifieddate>|<netsuite id>"` (id breaks same-second
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.751",
3
+ "version": "1.0.752",
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",