toga-ai 1.0.602 → 1.0.604

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-08-17
9
+ updated: 2026-08-18
10
10
  owners: ["dfranks", "bala", "jcardinal", "mhammontree", "snaredla"]
11
11
  files:
12
12
  - worker/crons/toga2/netsuite/common_sync_togasupply.php
@@ -27,6 +27,7 @@ related:
27
27
  - ../../library/features/toga2-api-client-and-bridge.md
28
28
  - ../../../2.0/apps/_underscore/features/item-fulfillment-stage-lifecycle-and-order-status.md
29
29
  - ../../../clients/elite/features/netsuite-togasupply-sync.md
30
+ - ../../../clients/compass-usa/features/sales-order-line-renumbering.md
30
31
  ---
31
32
 
32
33
  ## Summary
@@ -402,9 +403,46 @@ Parameters are stored **per client DB** but accessed **through the TOGa2 API**,
402
403
  `itemFulfillmentItems` or `invoiceItems` reverse-hasMany collections (only the PO-link bridges), so
403
404
  the fix keys off the recorded "could-not-delete" set rather than trusting those collections — see
404
405
  [App_Api_Toga2 client & bridge](../../library/features/toga2-api-client-and-bridge.md).
406
+ - **⚠ A second, distinct poison-order variant froze SALES_ORDERS again on SO 14106 (fixed
407
+ 2026-08-18) — this one was on the api2/2.0 side, not in `library`.** Fixing the 2026-08-17
408
+ renumber-DELETE bug let Compass USA's sync advance past SO 7447 (`06:13:26`) to SO **14106**
409
+ (`06:30:41`), which then threw for a completely different reason: the api2 `PUT /sales-orders`
410
+ fired `_Model_Compass_SalesOrder::postPut()` → `_Model_Compass_SalesOrderItem::renumberLineNumbers()`,
411
+ whose **single-UPDATE renumber raised MySQL 1062 duplicate-key** on any order whose `id`-order ≠
412
+ `lineNumber`-order (SO 14106: `id 86674` line 19→6 collided with `id 86675` still on 6). The 1062
413
+ fataled the api2 request, `App_Api_Toga2::send()` threw, and — same as before — the
414
+ `try/catch`-less SALES_ORDERS loop aborted the whole section:
415
+ `NETSUITE_EXECUTION_MODE_SALES_ORDERS='1-RUNNING'`, watermark frozen at `2026-08-12 06:30:41`
416
+ (~6 days), other five sections healthy at `432000-IDLE`. **Root-cause fix is in `_underscore`
417
+ (2.0), not this worker** — the renumber is now two-phase (park negative, flip positive); full
418
+ detail in [Compass sales-order line-number renumbering](../../../clients/compass-usa/features/sales-order-line-renumbering.md).
419
+ The worker/1.0 side is only the trigger path (unchanged). **Lesson: the missing SALES_ORDERS
420
+ per-record `try/catch` means every future api2-side write error on a single order re-freezes the
421
+ section** — the poison-order class is not closed by fixing individual orders.
422
+ - **⚠ Diagnostic trap: a fatal api2 PUT leaves NO row in `Logs_<Client>.Api` — check `Logs.Issue`
423
+ instead.** When the api2 request fatals *before* the request-logging middleware (as the 1062
424
+ renumber collision does), it writes **nothing** to `Logs_Compass.Api`. Do **not** conclude "no
425
+ failing api2 call happened" from an empty per-client Api log. The error surfaces only in the shared
426
+ `Logs.Issue` tracker (here **Issue 246**, first seen 2026-08-17 22:16), with the full trace
427
+ pointing at `renumberLineNumbers ← postPut ← V2.php processRoutePairs`. Localize the stalled
428
+ section from `Client_<Client>.Parameters` (`NETSUITE_EXECUTION_MODE_*` RUNNING-vs-IDLE +
429
+ `NETSUITE_LAST_SYNC_DATETIME_*` frozen watermark), then read `Logs.Issue` — not `Logs_<Client>.Api`
430
+ — for the throwing order.
405
431
 
406
432
  ## Change history
407
433
 
434
+ - 2026-08-18 — Recorded the **next poison-order variant** that re-froze Compass USA's SALES_ORDERS
435
+ section on SO **14106** right after the 2026-08-17 fix let the sync advance past SO 7447. This one
436
+ is 2.0-side: the api2 `PUT /sales-orders` → `postPut` → `renumberLineNumbers()` **single-UPDATE**
437
+ renumber threw MySQL **1062** on any order whose `id`-order ≠ `lineNumber`-order, fataling the api2
438
+ request; the `try/catch`-less SALES_ORDERS loop aborted the section (mode `1-RUNNING`, watermark
439
+ stuck `2026-08-12 06:30:41`, ~6 days). Root-cause fix is in **`_underscore`** (two-phase renumber —
440
+ see the [renumbering doc](../../../clients/compass-usa/features/sales-order-line-renumbering.md));
441
+ this worker is only the trigger and is unchanged. Also recorded the **diagnostic trap**: the fatal
442
+ PUT wrote **no** `Logs_Compass.Api` row (it died before the logging middleware), so the failure was
443
+ visible only in shared `Logs.Issue` (Issue 246) — do not read an empty per-client Api log as "no
444
+ failing call." Reinforced that the missing SALES_ORDERS per-record `try/catch` keeps the
445
+ poison-order class open. Diagnosis only on this repo (read-only). (jcardinal)
408
446
  - 2026-08-17 — Fixed a Compass USA ~5-day SALES_ORDERS freeze: an **invoiced** line that NetSuite
409
447
  renumbered could not be DELETEd (FK `InvoiceItems.salesOrderItemId` `RESTRICT` → MySQL 1451 →
410
448
  api2 EV-11), and because the SALES_ORDERS loop has **no per-record `try/catch`** the throw aborted
@@ -7,7 +7,7 @@ client: shared
7
7
  type: feature
8
8
  status: active
9
9
  updated: 2026-08-03
10
- owners: ["jcardinal", "bala", "mhammontree"]
10
+ owners: ["jcardinal", "bala", "mhammontree", "apeterson"]
11
11
  files:
12
12
  - _underscore/Model/Client/EmailTemplate.php
13
13
  - _underscore/Model/Client/EmailTemplateOutgoingEmailAddress.php
@@ -98,6 +98,16 @@ can keep using `sendEmail($api, ...)`.
98
98
  2026-06-15 the only entry point was `sendEmail(&$api, ...)`, so callers with no API
99
99
  context faked one: `$api = (object)['client' => (object)['clientIdentifier' => …]]`.
100
100
  That hack is obsolete — pass the identifier to `send()` instead.
101
+ - **⚠ Template vars MUST be spread into the variadic, never passed as a bare array.** Both
102
+ `sendEmail(&$api, $uuid, $to, $cc, $bcc, ...$args)` and `send(...)` collect the template
103
+ variables via `...$args`. Pass them **spread** so PHP 8.1+ named-argument collection yields
104
+ string keys/string values (`sendEmail($api, $uuid, $to, [], [], ...['userName'=>$name, ...])`
105
+ → `$args = ['userName'=>$name, ...]`). Passing the map as a single positional argument
106
+ (`sendEmail($api, $uuid, $to, [], [], $vars)`) makes `$args = [0 => [...]]` — a numeric key
107
+ whose value is an *array*. `replaceTemplateVariables()` then runs `str_replace('{0}', <array>, …)`
108
+ → `TypeError: str_replace(): Argument #2 must be of type string, array given`, surfacing as a
109
+ production 500 / **EO-1**. `Model/Compass/*` gets this right (spread); the four `Model/Quad/*`
110
+ call sites were copied from Compass but dropped the `...` — fixed 2026-08-18.
101
111
  - **`sendEmail()`'s signature is load-bearing for scripted APIs** — the Record Script engine
102
112
  (`api2/Component/Api/V2/V2.php`, ~line 3594) calls the method with `api` as a named
103
113
  argument, so the first param must stay `&$api`. Do not "clean it up" by removing it.
@@ -151,6 +161,12 @@ worker method) in-process instead.
151
161
 
152
162
  ## Change history
153
163
 
164
+ - 2026-08-18 — Fixed the Quad order-approval/rejection emails' production 500
165
+ (`str_replace(): Argument #2 must be string`, EO-1): the four `Model/Quad/ApprovalDecision.php`
166
+ + `Model/Quad/SalesOrder.php` call sites passed the template-vars map as a bare positional
167
+ array, so `...$args` collected `[0 => [...]]` and `replaceTemplateVariables()` fed an array to
168
+ `str_replace`. Fix = spread the array (`...[...]`) to match the working `Model/Compass/*`
169
+ pattern. Added the **"template vars must be spread, never a bare array"** gotcha. (apeterson)
154
170
  - 2026-08-03 — Refined the `CharSet` gotcha: the **transmit** side doesn't set it either, so the
155
171
  effective wire charset is **iso-8859-1**, and a body that needs accents should escape with
156
172
  `htmlentities($v, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', false)` (details on
@@ -6,7 +6,7 @@
6
6
  | [API Payload Interceptors (metadata-registered prePost/postPut model hooks)](features/api-payload-interceptors.md) | A `_Model`'s `prePost()` / `postPost()` / `prePut()` / `postPut()` hooks are **not** called by the model. | api2/Component/Api/V2/V2.php, api2/Controller/Index.php, _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Compass/PurchaseOrder.php, _underscore/Model/Elite/SalesOrder.php, _underscore/Model/Elite/ServiceRequest.php, _underscore/Model/Compass/SalesOrder.php, _underscore/Model/Compass/SalesOrderItem.php, dbchanges2/Core/2026-06-30a - ItemFulfillmentStageDefaultInterceptor.sql, dbchanges2/Core/2026-08-06a - Service request and sales order payload interceptors.sql, dbchanges2/Client_Compass/2026-08-06 - RemoveBrokenSalesOrderItemPostPostInterceptor.sql, dbchanges2/Client_Compass/2026-08-14a - AddSalesOrderInactiveStandaloneItemPreInterceptors.sql, dbchanges2/Client_CompassCanada/2026-08-14a - AddSalesOrderInactiveStandaloneItemPreInterceptors.sql |
7
7
  | [Multi-Client (Cross-Client) Data Retrieval](features/cross-client-data-retrieval.md) | A single authenticated V2 GET listing can return records across **many** clients (designed for 1000+) that the caller is entitled to, honoring **each target cli | api2/Component/Api/CrossClient/CrossClient.php, api2/Component/Api/V2/V2.php, api2/Controller/Index.php, _underscore/Model/Cache/Table.php, _underscore/Model/Cache/Tables/Client.php, _underscore/Model/Core/Record.php, worker2/Worker/Platform/Cache.php, worker2/Controller/Index.php, worker2/_.php, dbchanges2/Cache/2026-06-30a - MultiClientCacheTables.sql, dbchanges2/Core/2026-06-30b - CacheClusterRegistrationAndRecordTtl.sql, dbchanges2/Core/2026-07-27a - PlatformCacheCleanCron.sql |
8
8
  | [Encrypted-User-UUID Auth Handoff (/auth/encrypted-user-uuid)](features/encrypted-user-uuid-auth-handoff.md) | `POST /auth/encrypted-user-uuid` is the intended **cross-client / SSO-handoff identity mechanism**: given an encrypted `{client, user}` UUID pair, it mints a fr | api2/Component/Api/CrossClient/CrossClient.php |
9
- | [ENVIRONMENT (not the EB environment name) decides the _underscore branch and Config file](features/environment-variable-drives-underscore-branch.md) | An api2 Elastic Beanstalk instance decides **which `_underscore` branch it clones** and **which `Config/<env>.ini` it loads** from the EB environment property * | api2/.ebextensions/git.php, api2/.ebextensions/php_include_underscore.config, api2/.ebextensions/git.sandbox-dev.json, api2/Config/beta.ini, api2/Config/sandbox-dev.ini, api2/Component/Api/V2/V2.php |
9
+ | [ENVIRONMENT (not the EB environment name) decides the _underscore branch and Config file](features/environment-variable-drives-underscore-branch.md) | An api2 Elastic Beanstalk instance decides **which `_underscore` branch it clones** and **which `Config/<env>.ini` it loads** from the EB environment property * | api2/.ebextensions/git.php, api2/.ebextensions/php_include_underscore.config, api2/.ebextensions/git.sandbox-dev.json, api2/.ebextensions/git.sandbox-client.json, api2/Config/beta.ini, api2/Config/sandbox-dev.ini, api2/Component/Api/V2/V2.php |
10
10
  | [Health-check endpoint (/health liveness short-circuit)](features/health-check-endpoint.md) | `_Controller_Index::api()` short-circuits **liveness/health-probe** requests to an HTTP 200 **before** any routing, DB bootstrap, or V2 engine work runs. | api2/Controller/Index.php |
11
11
  | [Language Translation Layer (audience.language + sidecar tables)](features/language-translation-layer.md) | Serves the same TOGa data (Item title/description/longDescription, plus item **feature** text — `Features.name`, `ItemCategoryFeatureGroups.name`, `ItemFeatures | api2/Component/Api/V2/V2.php, api2/Component/Api/V2/Response/Response.php, _underscore/Model/Core/Setting.php, _underscore/Model/Core/RecordField.php, _underscore/Model/Core/DefaultGlobalSetting.php, _underscore/Model/Client/ItemTranslation.php, _underscore/Model/Client/FeatureTranslation.php, _underscore/Model/Client/ItemCategoryFeatureGroupTranslation.php, _underscore/Model/Client/ItemFeatureTranslation.php, dbchanges2/Client/2026-06-23a - ItemTranslations.sql, dbchanges2/Client/2026-06-23b - ItemTranslationsAcl.sql, dbchanges2/Client/2026-07-13a - FeatureTranslations.sql, dbchanges2/Client/2026-07-13b - FeatureTranslationsAcl.sql, dbchanges2/Core/2026-06-23a - RecordFieldsTranslationColumn.sql, dbchanges2/Core/2026-06-23b - ItemTranslationsRecord.sql, dbchanges2/Core/2026-07-13 - FeatureTranslationsRecord.sql |
12
12
  | [/auth/login resolves the client from the email domain, not the Bearer token (cross-client user path)](features/login-cross-client-user-resolution.md) | `POST /v2/auth/login` (email/password user login) can silently swap the target client mid-request. | api2/Component/Api/V2/V2.php, _underscore/String.php |
@@ -7,11 +7,12 @@ client: shared
7
7
  type: feature
8
8
  status: active
9
9
  updated: 2026-08-06
10
- owners: ["bala", "mhammontree"]
10
+ owners: ["bala", "mhammontree", "apeterson"]
11
11
  files:
12
12
  - api2/.ebextensions/git.php
13
13
  - api2/.ebextensions/php_include_underscore.config
14
14
  - api2/.ebextensions/git.sandbox-dev.json
15
+ - api2/.ebextensions/git.sandbox-client.json
15
16
  - api2/Config/beta.ini
16
17
  - api2/Config/sandbox-dev.ini
17
18
  - api2/Component/Api/V2/V2.php
@@ -116,12 +117,36 @@ one.
116
117
  `_underscore`. (Contrast: the 1.0 **tools** app works cleanly because its helper *and* its config
117
118
  live in the same repo, and it clones `library` from a **fixed** branch, `_production`, rather
118
119
  than an `ENVIRONMENT`-derived one.)
120
+ - **⚠ A `git.<env>.json` `"branch"` key can pin a tier to the WRONG `_underscore` branch, silently.**
121
+ When a code fix pushed to a branch does not appear after an api2 deploy, before debugging the
122
+ code verify which branch that EB environment's `.ebextensions/git.<env>.json` actually clones:
123
+ a present `"branch"` value overrides the `'_' . strtolower(ENVIRONMENT)` default. **Concrete
124
+ (fixed 2026-08-18):** `git.sandbox-client.json` had `"branch": "_production"`, so the
125
+ `sandbox-client` environment ran `_underscore` from `_production` — never `_sandbox-client` —
126
+ which is why fixes merged to `_sandbox-client` never showed up there. Confirmed two ways: the
127
+ config file value, and live runtime behavior matching `_production` (unfixed) rather than
128
+ `_sandbox-client` (fixed). Fix = remove the `"branch"` key so it falls back to `_sandbox-client`.
129
+ - **Deploy path is build-time, so a git push alone does not update running instances.** The
130
+ `_underscore` clone happens in the prebuild hook at build time: commit the fix on the feature
131
+ branch, merge that branch into the deploy-channel branch (`_` + `ENVIRONMENT`, e.g.
132
+ `_sandbox-client`), push, **then redeploy the EB environment** to re-run the clone.
133
+ - **⚠ SECURITY — every `.ebextensions/git.*.json` carries a plaintext GitHub PAT** committed in
134
+ the repo (used to clone `_underscore`). Pre-existing leak; treat the committed tokens as
135
+ compromised, rotate, and move them to EB environment properties / SSM. Do **not** copy the token
136
+ value anywhere — this note records only that the credential lives in these files.
119
137
  - **Duplicate integration code is the trap this creates.** When the same capability is needed in
120
138
  both tiers it must exist twice (once per framework) — keep the credential/config location
121
139
  documented in both places so a rotation updates both.
122
140
 
123
141
  ## Change history
124
142
 
143
+ - 2026-08-18 — Recorded a second concrete `"branch"`-pin trap: `git.sandbox-client.json` pinned
144
+ `"branch": "_production"`, so the `sandbox-client` EB environment cloned `_underscore` from
145
+ `_production` and never received `_sandbox-client` merges (confirmed via both the config value and
146
+ live runtime matching the unfixed `_production` code); fix = drop the `"branch"` key. Added the
147
+ general diagnostic ("fix not appearing after deploy → check `git.<env>.json` branch pin first"),
148
+ the build-time deploy note (merge into `_`+ENVIRONMENT then redeploy — a push alone is inert),
149
+ and the ⚠ plaintext-GitHub-PAT-in-`git.*.json` security gotcha (location only). (apeterson)
125
150
  - 2026-08-06 — TRUE-80282 deploy: independently re-confirmed the mapping from a second tier
126
151
  (`api.beta.togahub.com` = `ENVIRONMENT=beta` → `Config/beta.ini` → branch `_beta`) at a cost of two
127
152
  deploy cycles, and recorded **why** the branch derives: there is deliberately **no
@@ -6,8 +6,8 @@ project: _Underscore
6
6
  client: compass-usa
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-08-11
10
- owners: ["bala"]
9
+ updated: 2026-08-18
10
+ owners: ["bala", "jcardinal"]
11
11
  files:
12
12
  - _underscore/Model/Compass/SalesOrderItem.php
13
13
  - _underscore/Model/Compass/SalesOrder.php
@@ -43,6 +43,29 @@ Live production wiring (`ApiPayloadInterceptors`, Compass):
43
43
  Add/edit renumbering rides the **parent** (`sales-orders`) hooks; delete renumbering rides the
44
44
  **child**'s `preDelete`.
45
45
 
46
+ ### How the renumber SQL must run — two phases, never one UPDATE (2026-08-18)
47
+
48
+ Lines are numbered `1..N` by `ROW_NUMBER() OVER (ORDER BY id)`, but the write **cannot** be a
49
+ single `UPDATE … JOIN (SELECT id, ROW_NUMBER() … AS rowNumber) SET lineNumber = rowNumber`.
50
+ `SalesOrderItems` carries `UNIQUE(salesOrderId, lineNumber)`, which MySQL enforces **per row as
51
+ each row updates**, not deferred to statement end. Whenever `id`-order ≠ current `lineNumber`-order,
52
+ a row that takes a number another **not-yet-updated** row still holds raises **MySQL 1062
53
+ duplicate-key** mid-statement and the whole UPDATE (and its request) fatals.
54
+
55
+ > Observed: SO **14106**, `id 86674` moving line 19→**6** collided with `id 86675` still sitting on
56
+ > line 6 → `Duplicate entry '14106-6' for key 'salesOrderId'`.
57
+
58
+ The collision-free form (same numbering outcome, `ROW_NUMBER() OVER (ORDER BY id)` unchanged) is a
59
+ **two-phase** renumber:
60
+
61
+ 1. **Park negative** — `SET lineNumber = -Temporary.rowNumber` for every line. Negatives never
62
+ collide with the positive originals or finals, and each `-rowNumber` is distinct.
63
+ 2. **Flip positive** — `SET lineNumber = -lineNumber WHERE … AND lineNumber < 0`.
64
+
65
+ Values only ever range `-N..N`, safe for the `SMALLINT` `lineNumber` column. Both UPDATEs run in
66
+ the **same api2 request → same `DB_CLIENT` transaction**, so the intermediate negative state is
67
+ never externally visible and the pair rolls back together on failure. Signature unchanged (PHP 8.1).
68
+
46
69
  ## Why delete-time renumbering CANNOT be a `postDelete`
47
70
 
48
71
  A `postDelete` interceptor can never fire at all. api2's post-processing interceptor block runs
@@ -87,9 +110,42 @@ fire reliably as the request's **first-resolved record**. Verified healthy after
87
110
  subclasses inherit it. Do not add per-region copies.
88
111
  - **These interceptor rows have no route and no audit trail.** They are direct SQL only; nothing
89
112
  records who added or removed one. Snapshot Compass's row set into the ticket before changing it.
113
+ - **⚠ The renumber must never be a single collision-prone UPDATE.** `UNIQUE(salesOrderId,
114
+ lineNumber)` is enforced per-row mid-statement, so a one-shot `SET lineNumber = rowNumber` throws
115
+ MySQL 1062 the moment any row takes a number a not-yet-updated row still holds. Renumber in two
116
+ phases (park negative, then flip positive) — see *How it works*.
117
+ - **Both Compass regions are affected.** `renumberLineNumbers()` lives on the shared
118
+ `_Model_Compass_SalesOrderItem` base, so **Compass Canada** (`compass-canada`) inherits the same
119
+ bug and the same fix; there is no per-region copy to touch.
120
+ - **⚠ The `preDelete` (delete-a-middle-line) path is a pre-existing latent collision — unchanged by
121
+ this fix.** When a middle line is deleted, the row being removed keeps its positive `lineNumber`
122
+ during the renumber, so `preDelete` can still hit the same 1062 as the old add/edit code did. It
123
+ is healthy in practice only because deletes are almost always the **last** line. If middle-line
124
+ deletes become common, `preDelete` needs the same two-phase treatment.
125
+ - **This was not a one-order anomaly.** As of 2026-08-18, **38,947 of 105,169** Compass orders (37%)
126
+ have `lineNumber`-order ≠ `id`-order (`ROW_NUMBER() OVER (PARTITION BY salesOrderId ORDER BY id)`)
127
+ — any of them ran the buggy renumber on its next PUT/edit and could collide. No mass data retrofix
128
+ is needed for the freeze: the two-phase renumber normalizes each order as the sync re-touches it.
129
+ A post-deploy sweep of that set could confirm whether any were left with gap'd numbering (which
130
+ MITS/PO linking depends on — see [MITS PO → SO item linking](./mits-po-to-so-item-linking.md))
131
+ from silently-rolled-back renumbers.
90
132
 
91
133
  ## Change history
92
134
 
135
+ - 2026-08-18 — **Fixed** the `renumberLineNumbers()` single-UPDATE **1062 duplicate-key collision**.
136
+ The renumber ran as one `UPDATE … JOIN (SELECT id, ROW_NUMBER() OVER (ORDER BY id) AS rowNumber)
137
+ SET lineNumber = rowNumber`; because `UNIQUE(salesOrderId, lineNumber)` is enforced per-row
138
+ mid-statement, any order whose `id`-order ≠ `lineNumber`-order fataled with `Duplicate entry`
139
+ (observed SO 14106, `id 86674` line 19→6 vs `id 86675` still on line 6). Replaced with a
140
+ **two-phase** renumber (Phase 1 parks each line at `-rowNumber`, Phase 2 flips positive) that keeps
141
+ the exact `ROW_NUMBER() OVER (ORDER BY id)` outcome, never collides, stays within the `SMALLINT`
142
+ range, and runs both UPDATEs in the same api2 `DB_CLIENT` transaction (atomic, no visible negative
143
+ state). Shared base class → **Compass Canada inherits the fix**. Systemic scope: 38,947/105,169
144
+ (37%) Compass orders currently have out-of-order numbering and were all exposed. Noted the
145
+ pre-existing `preDelete` middle-line-delete path can still collide identically (unchanged, latent).
146
+ This collision was the root cause of the Compass USA SALES_ORDERS sync freeze on SO 14106 — cascade
147
+ + diagnosis recorded in the [NetSuite → TOGa Supply per-client sync](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md).
148
+ (Fix confirmed live in prod; local working tree still shows the pre-fix single UPDATE.) (jcardinal)
93
149
  - 2026-08-11 — Documented the as-built wiring and the reasoning behind it after a production
94
150
  post-mortem (investigation only, no code change): add/edit renumbering hangs off
95
151
  `_Model_Compass_SalesOrder::postPost`/`postPut` (record **14** `sales-orders`), delete-time
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.602",
3
+ "version": "1.0.604",
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",