toga-ai 1.0.861 → 1.0.863

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.
@@ -800,6 +800,11 @@ nested count:
800
800
  fetch returned `salesOrderItems` truncated (observed cap **25**); an order with >25 lines dropped a
801
801
  line the fulfillment referenced, the match failed, and the section fail-loud-froze. The flat list
802
802
  replaces the truncated nested one before match/reconstruct.
803
+ - **Existing `ItemFulfillmentItems` for the dedup lookup (added 2026-09-21)** — the create-vs-update
804
+ dedup read the truncated **nested** `itemFulfillmentItems` off a `depth=4` header GET, so it missed
805
+ every line past **25** and **re-created duplicate `ItemFulfillmentItems` every run**. Now paged flat
806
+ (`recordsPerPage=1000`, `$maxPages=500` cap, on `meta->nextPage`). api2's unpaged cap is
807
+ `DEFAULT_RECORDS_PER_PAGE = 25` (`api2/Component/Api/V2/V2.php:27`).
803
808
 
804
809
  ### Retiring the stale SalesOrder a reclassified TransferOrder leaves behind (DEPLOYED + RUNNING 2026-09-18)
805
810
 
@@ -1197,6 +1202,16 @@ library (or vice versa) crashes GroWrk and Adyen on their next sync run.
1197
1202
 
1198
1203
  ## Gotchas / known issues
1199
1204
 
1205
+ - **PLATFORM FACT: NO TOGA client fulfills NetSuite item groups (bundles).** Every client fulfills the
1206
+ **member items individually**. This is the load-bearing safety assumption behind the IA dedup's
1207
+ empty-serials delete branch (see [NYCHH IA→fulfillment link](../../../clients/nychh/features/netsuite-inventory-adjustment-fulfillment-link.md))
1208
+ and any item-group discussion. If a client ever starts fulfilling item groups, revisit every dedup
1209
+ that keys on product/order-line alone.
1210
+ - **⚠ OPEN — the downstream fulfillment→SO match in `syncItemFulfillmentFromNetsuite` still keys by
1211
+ parent order-line uuid ALONE.** The reconcile map build (`toga2.php` ~L6088-6092) and the two match
1212
+ sites (~L6352 / ~L6387) key on parent order-line uuid only. Harmless today (no client fulfills item
1213
+ groups — see the platform fact above), but for any client that ever fulfills item-group bundles this
1214
+ risks item-group churn / a possible wrong-qty overwrite. Not fixed.
1200
1215
  - **OPEN, not root-caused (2026-09-08): a duplicate item INSERT aborts the whole NYCHH sales-order
1201
1216
  run.** A run created `Client_Nychh.Items` id **5429** (`partNumber` 658-BFZH, `manufacturerId` 26,
1202
1217
  `catalogId` 1, `c_netsuiteInternalItemId` 1617692) at 16:22:09 and then tried to insert **the same
@@ -2047,6 +2062,15 @@ library (or vice versa) crashes GroWrk and Adyen on their next sync run.
2047
2062
  [NetSuite Sync Alert Monitor](../../library/features/netsuite-sync-alert-monitor.md).
2048
2063
 
2049
2064
  ## Change history
2065
+ - 2026-09-21 — **NYCHH INVENTORY_ADJUSTMENTS unfrozen (library, deployed + prod-verified).** Paged the
2066
+ existing-`ItemFulfillmentItems` dedup lookup in `syncItemFulfillmentFromNetsuite` (it read a truncated
2067
+ nested collection off a `depth=4` header GET past the 25-row cap and re-created duplicate
2068
+ `ItemFulfillmentItems` every run). Recorded the platform fact that **no TOGA client fulfills NetSuite
2069
+ item groups**, and the open follow-up that the fulfillment→SO reconcile map still keys on parent
2070
+ order-line uuid alone. The IA→fulfillment attach fixes (equality rule removed, product-only link,
2071
+ no-line skip, `deleteItemFulfillmentDuplicateLines`) live on the
2072
+ [NYCHH IA→fulfillment link doc](../../../clients/nychh/features/netsuite-inventory-adjustment-fulfillment-link.md).
2073
+ INVENTORY_ADJUSTMENTS reached IDLE, cursor draining, `Logs.Issue` 827 cleared. (jcardinal)
2050
2074
  - 2026-09-21 — **Two NYCHH sync freezes fixed, deployed (library + worker together) and prod-verified.**
2051
2075
  (1) SALES_ORDERS froze (`Logs.Issue` 813) on a stale transfer-order invoice the retirement couldn't
2052
2076
  move (`Invoices` has no `transferOrderId`). New `deleteStaleTransferOrderInvoicesForSalesOrder()`
@@ -55,7 +55,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
55
55
  - **Endeavor Health** (`endeavor-health`) → [clients/endeavor-health/INDEX.md](clients/endeavor-health/INDEX.md)
56
56
  - **Fordham University** (`fordham`) → [clients/fordham/INDEX.md](clients/fordham/INDEX.md)
57
57
  - **GroWrk** (`growrk`) → [clients/growrk/INDEX.md](clients/growrk/INDEX.md)
58
- - **Managelife** (`managelife`) → [clients/managelife/INDEX.md](clients/managelife/INDEX.md)
58
+ - **ManageLife** (`managelife`) → [clients/managelife/INDEX.md](clients/managelife/INDEX.md)
59
59
  - **Northwell Health** (`northwell`) → [clients/northwell/INDEX.md](clients/northwell/INDEX.md)
60
60
  - **New York City Department of Education** (`nycdoe`) → [clients/nycdoe/INDEX.md](clients/nycdoe/INDEX.md)
61
61
  - **NYC Health & Hospitals** (`nychh`) → [clients/nychh/INDEX.md](clients/nychh/INDEX.md)
@@ -1,5 +1,5 @@
1
- # Client: Managelife `managelife`
1
+ # Client: ManageLife `managelife`
2
2
 
3
3
  | Doc | Framework | Summary |
4
4
  |-----|-----------|---------|
5
- | [Managelife](profile.md) | 2.0 | Managelife 2.0 platform tenant (`Client_Managelife`). |
5
+ | [ManageLife](profile.md) | 2.0 | ManageLife 2.0 platform tenant (`Client_Managelife`). |
@@ -1,5 +1,5 @@
1
1
  ---
2
- title: "Managelife"
2
+ title: "ManageLife"
3
3
  framework: "2.0"
4
4
  apps:
5
5
  - dbchanges2
@@ -14,7 +14,7 @@ related:
14
14
  - ../../2.0/apps/_underscore/features/client-user-contactid-required.md
15
15
  ---
16
16
 
17
- Managelife 2.0 platform tenant (`Client_Managelife`). Stub profile — expand as work touches it. Formal client name not yet confirmed.
17
+ ManageLife 2.0 platform tenant (`Client_Managelife`). Stub profile — expand as work touches it.
18
18
 
19
19
  ## Summary
20
20
 
@@ -59,8 +59,12 @@ Like [NYCHH transfer-order import](./netsuite-transfer-order-import.md), the bri
59
59
 
60
60
  Registered in `dbchanges2` this session: records **363/364** + `RecordFields` **2608-2615** + Super User ACL (`Core/2026-09-21a`); old fields **2517/2521** retired (`Core/2026-09-21b`); both bridge tables created all-client + existing 1-to-1 backfilled (`Client/2026-09-21a`); the Base+API four-table ACL chain on 363/364 (`Client/2026-09-21b`); old FK columns dropped, guarded/re-runnable (`Client/2026-09-21c`). The FK declarations were removed from `Model/Client/InventoryAdjustment.php` + `InventoryAdjustmentItem.php`.
61
61
 
62
- ### Line matching — by PRODUCT quantity, not a direct link
63
- NetSuite has **no** direct item-fulfillment-item ↔ adjustment-item link. Match by **product**: the sum of a product's fulfillment-item quantity must equal that product's `quantityAdjustBy` on the adjustment (verified 16/16 on adj 1320). Link **product-to-product**, which handles a product that appears on more than one adjustment line. **FAIL LOUD** on any per-product quantity mismatch — never guess a pairing.
62
+ ### Line matching — by PRODUCT ONLY (no quantity-equality)
63
+ NetSuite has **no** direct item-fulfillment-item ↔ adjustment-item link. Match by **product**: link every fulfillment line whose product matches an adjustment line's product. Link **product-to-product**, which handles a product that appears on more than one adjustment line.
64
+
65
+ **⚠ The old "quantities must be EQUAL" rule was WRONG and froze the feed on valid data (removed 2026-09-21).** An inventory adjustment corrects stock (recount / damage / shrinkage) **independently** of what shipped, so the adjustment `quantityAdjustBy` need NOT equal the fulfillment quantity. Proven in NetSuite: adj **6868145** adjusts **400** of `GM-394-SE_` while its referenced fulfillment (SO #259330) shipped **350**. The equality check is removed — do NOT reinstate it. The link is by product; quantity is not part of the match.
66
+
67
+ **A product on the adjustment with NO line on the linked fulfillment LOGS and skips that product (2026-09-21) — it does not throw.** Proven legitimate: adj **6966700** lists `SVC-FS-ENROLLWIN-GW_`, a **service** item on the adjustment that is never on the fulfillment (services aren't shipped). There is no fulfillment line to link a service to, so skipping is correct, not dropped data.
64
68
 
65
69
  ### Reconcile every run (self-healing, gated delete)
66
70
  Each run **reconciles** both bridges: add missing links, and delete only links NetSuite no longer lists — so a cursor reset re-produces correct data. The delete is **gated**: if a bridge row's key reads back **null** (a read/ACL gap), **throw** instead of deleting — never wipe good links on a partial read.
@@ -82,6 +86,12 @@ Lives in `library/app/api/toga2.php`, called from `syncInventoryAdjustmentFromNe
82
86
 
83
87
  **Loud failure (best-effort try/catch removed).** The attach and the surrounding NetSuite→DB sync path **no longer swallow errors**: the error-log-and-swallow `try/catch` blocks were removed so a data-corruption failure surfaces loudly (StarTech/Sentry catches left alone). Same loud-failure policy as the 2026-08-27 removal of the per-order `try/catch` skips across all six sync sections — see [per-client sync](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md). **Do not "soften" this back into a swallow** — a bridge/link miss must be visible, not silently dropped.
84
88
 
89
+ ### Unpaged reads truncate at 25 → the attach's reads are all paged (2026-09-21)
90
+ api2 caps an unpaged read at `DEFAULT_RECORDS_PER_PAGE = 25` (`api2/Component/Api/V2/V2.php:27`). All **three** reads in `attachInventoryAdjustmentToItemFulfillment` — the header bridge, the fulfillment-items read, and the line bridge — now **page** on `meta->nextPage` (`recordsPerPage=1000`, `$maxPages=500` cap), so a fulfillment with >25 of anything links completely. Same fix on the existing-items dedup lookup in `syncItemFulfillmentFromNetsuite` — see [per-client sync](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md); an unpaged dedup lookup missed rows past 25 and **re-created duplicate `ItemFulfillmentItems` every run**.
91
+
92
+ ### `deleteItemFulfillmentDuplicateLines()` — the IA attach cleans a linked fulfillment's duplicate lines BEFORE matching (2026-09-21)
93
+ The IA path does **not** re-run the fulfillment reconcile for an already-imported fulfillment, so that reconcile's self-heal never reaches truncation duplicates left on it. So the attach cleans them itself first. Dedup key = **`(parentOrderLineUuid|productUuid)`** PLUS a serial-assignment-id subset test: a later same-key row is deleted **only** when its `c_netsuiteInternalInventoryAssignmentId` set is **empty or a subset** of the kept row's — this spares NetSuite item-group members and removes only true truncation copies. A **circuit breaker** throws if >50% of lines are flagged; partial reads fail loud. The empty-serials delete branch is safe because [no TOGA client fulfills NetSuite item groups](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md) — every client fulfills the member items individually.
94
+
85
95
  ### On-demand dependency import (fulfillment not yet in TOGA) — now REST
86
96
  When the linked fulfillment has **not** been imported into TOGA yet, the attach **imports the fulfillment on demand** via `syncItemFulfillmentFromNetsuite` (which itself cascades to the originating sales order when missing), then **re-resolves and links**. This on-demand import was **converted from SOAP to REST** this session.
87
97
 
@@ -129,7 +139,10 @@ A **manual operator tool** in `worker/crons/toga2/netsuite/` (NEW): imports one
129
139
  - **Empty lookups drop or duplicate items.** The item loop filters to known warehouses and creates items on lookup-miss, so any manual/diagnostic invocation must seed the warehouse/item/manufacturer lookups (the harness does).
130
140
  - **A frozen NYCHH `INVENTORY_ADJUSTMENTS` section is NOT necessarily an item-matching / two-catalog problem (verified 2026-09-01).** When the section stuck at `1-RUNNING`, item matching was suspected (wrong of two intentional catalogs). Verified in prod it was fine: item **560** (uuid `4c7b93d2-…`, partNumber **B11B242201**, HYBRID) is the Agilant-catalog item and the **only** item with that part number — no duplicate second-catalog item, `$uuidItem` resolved correctly, and the inventory-adjustment item-create already stamps catalog `Agilant`. The freeze was purely the serial-CASE bug (fix (c) above). **Diagnosis chain (no NetSuite access):** `Client_Nychh.Parameters` (localize the stuck section) → `Logs.Event` `clientId=3` (crash `toga2.php:6080` via `common_sync_togasupply.php:1180` `syncInventoryAdjustmentFromNetsuite`, + `transactionId`) → `Logs_Nychh.Api` on that `transactionId` (untruncated `EV-10`/1062 rejection + payload).
131
141
 
142
+ - **⚠ OPEN — the IA dedup / self-heal deletes are NOT idempotent against already-gone children.** A half-finished prior run leaves a child already deleted; the retry's `send()` **throws on the 404** and wedges the run. Fix (not yet done): treat not-found as done in the delete path.
143
+
132
144
  ## Change history
145
+ - 2026-09-21 — Removed the wrong "adjustment qty must EQUAL fulfillment qty" match rule (an adjustment corrects stock independently of what shipped — proven adj 6868145: 400 adjusted vs 350 shipped); the IA→fulfillment line link is now by **PRODUCT only**, and a product with no fulfillment line **logs + skips** (service items, proven adj 6966700). Paged all 3 attach reads + the fulfillment dedup lookup (api2's 25-row unpaged cap was re-creating duplicate `ItemFulfillmentItems`). New `deleteItemFulfillmentDuplicateLines()` cleans a linked fulfillment's truncation duplicates before matching (serial-subset guard spares item-group members). Prod-verified: INVENTORY_ADJUSTMENTS reached IDLE, cursor draining, `Logs.Issue` 827 cleared. (jcardinal)
133
146
  - 2026-09-21 — Redesigned to **many-to-many**: `custbody_stock_adjustment` is multi-select (one adjustment → many fulfillments), so two bridge tables (`ItemFulfillments_InventoryAdjustments` rec 363, `ItemFulfillmentItems_InventoryAdjustmentItems` rec 364) replace the single FK columns; attach loops all fulfillments, line-matches by product quantity (fail-loud), reconciles each run with a gated delete. Prod-verified on adj 1320 (17 header + 16/16 line). (jcardinal)
134
147
  - 2026-09-03 — Adjustment→fulfillment link moved to its own `IS_ENABLED_INVENTORY_ADJUSTMENT_FULFILLMENT_LINK` flag, independent of transfer-order detection. (jcardinal)
135
148
  - 2026-08-29 — Migrated the sync + on-demand import from SOAP to REST. (jcardinal)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.861",
3
+ "version": "1.0.863",
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",