toga-ai 1.0.989 → 1.0.991

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.
@@ -9,6 +9,7 @@
9
9
  | [App_Sso — Reusable 1.0 SSO Initiation (SP-initiated SAML via saml.togahub.com)](features/app-sso-initiation.md) | Reusable 1.0 SP-initiated SAML SSO via `saml.togahub.com` (`App_Sso::initiate`); the canonical doc to follow for any "add SSO to `<1.0 app>`" request. |
10
10
  | [Carrier detection from a tracking number (App_Misc) — patterns, check digits, shipping-method fallback](features/carrier-detection-from-tracking-number.md) | How 1.0 decides which carrier a tracking number belongs to (`App_Misc`, `library/app/misc.php`): certain vs shared-shape patterns, the FedEx check digit, and th |
11
11
  | [Cron Execution Monitoring (App_Framework check-in/out → CronJobExecutions)](features/cron-execution-monitoring.md) | 1.0 cron check-in/out lifecycle via `App_Framework::cronInitialization()`/`cronFinished()` → `Common.CronJobExecutions`; open to monitor a 1.0 cron or read its |
12
+ | [Customer PO lines from NetSuite — duplicate-line merge + quantity from sales orders (SO-QTY)](features/customer-po-line-merge-and-quantity.md) | Two `App_Api_Toga2` steps that keep upstream customer PO lines clean: a merge step (deletes empty duplicates, merges same item + same cost) and an opt-in SO-QTY |
12
13
  | [1.0 MySQL Connection Lifecycle — wait_timeout, the 2006 Reconnect Gap, keepAlive/reconnect/queryForked](features/db-connection-lifecycle-and-reconnect.md) | How a 1.0 mysqli connection dies during a long external call ("MySQL server has gone away"), why the built-in 2006 reconnect never fired, and the three helpers |
13
14
  | [Diagnostic Dialog — View Recommended Services Routing](features/diagnostic-dialog-view-recommended-services.md) | TOGa Refresh 2026 "View Recommended Services" routing — per-device diagnostic `resultCode` decides serviceselection vs techsupport; open when touching that rout |
14
15
  | [Elite Freshservice Sync (library)](features/elite-freshservice-sync.md) | Elite bidirectional ticket/attachment sync between TOGA 2 and TOGaDesk (`App_Api_Toga2::syncWithTogadesk`); open for the sync mechanics, watermarks, and Elite-s |
@@ -0,0 +1,65 @@
1
+ ---
2
+ title: "Customer PO lines from NetSuite — duplicate-line merge + quantity from sales orders (SO-QTY)"
3
+ framework: "1.0"
4
+ repo: library
5
+ project: Library
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-10-09
10
+ owners: [jcardinal]
11
+ files:
12
+ - library/app/api/toga2.php
13
+ - worker/crons/toga2/netsuite/sync_togasupply_hh.php
14
+ - worker/crons/toga2/netsuite/process_netsuite_order.php
15
+ related:
16
+ - transfer-order-purchase-order-link-reconcile.md
17
+ - ../../worker/features/netsuite-togasupply-per-client-sync.md
18
+ - ../../worker/workflows/reprocessing-one-netsuite-order.md
19
+ - ../../../2.0/apps/_underscore/features/sales-order-purchase-order-bridge-direction.md
20
+ - ../../../clients/nychh/features/netsuite-transfer-order-import.md
21
+ ---
22
+
23
+ Two `App_Api_Toga2` steps that keep upstream customer PO lines clean: a merge step (deletes empty duplicates, merges same item + same cost) and an opt-in SO-QTY step (SO import reuses the same-price line and sets qty = sum of linked SO lines). Open before changing how customer PO lines are created, reused, merged, or sized.
24
+
25
+ ## Summary
26
+ - **Merge step** — `consolidateCustomerPurchaseOrderLines()`; runs inside [TO↔PO reconcile](transfer-order-purchase-order-link-reconcile.md), once per upstream customer PO, after the link sync. **Not client-gated** (every client that syncs TOs; Compass has 0 TO links so is never touched).
27
+ - **SO-QTY step** — in `syncSalesOrderFromNetsuite()`'s existing-customer-PO branch. **Per-client opt-in**: `CLIENT_CONFIGURATION['isEnabledCustomerPoLineQuantityFromSalesOrders']` (constant `CLIENT_CONFIGURATION_KEY_CUSTOMER_PO_LINE_QUANTITY_FROM_SALES_ORDERS`). On for **hh only**. Missing = off = old rule (reuse a line only on equal quantity).
28
+ - Cost compare = rounded to 2 decimals, everywhere.
29
+ - Regression tests exist (jcardinal's private test folder).
30
+
31
+ ## How it works
32
+ ### Merge step (`planCustomerPurchaseOrderLineMerge`, `isEmptyCustomerPurchaseOrderLine`)
33
+ A line is **empty** when it has no row in any table of `CUSTOMER_PO_LINE_REFERENCE_TABLES`: `PurchaseOrderItems_SalesOrderItems`, `SalesOrderItems_PurchaseOrderItems`, `PurchaseOrderItems_TransferOrderItems`, `TransferOrderItems_PurchaseOrderItems`, `ItemReceiptItems`, `AdvanceShippingNoticeItems`.
34
+ 1. Decide empties **before** merging. An empty line is deleted when its item has any non-empty line (any cost). All lines of an item empty → keep the lowest line, qty = sum.
35
+ 2. Non-empty lines merge only when **same item AND same cost**. Different price → separate lines.
36
+ 3. Merged qty = SUM.
37
+ 4. Never merge two stocked (SO-linked) lines. Never delete a line with a receipt or ASN.
38
+ 5. Write order (crash-safe): move links → delete → PUT qty. A 404 on delete → re-plan; max 3 passes.
39
+ 6. `NETSUITE_PO_LINE_MERGE_DRY_RUN` defined → logs `MERGE DRY-RUN`, writes nothing for this step.
40
+
41
+ ### Upstream matcher rank
42
+ `chooseReusablePurchaseOrderLine` ranks a PO line whose cost == the NetSuite TO rate right after "stocked" (full rank: [reconcile](transfer-order-purchase-order-link-reconcile.md)).
43
+
44
+ ### SO-QTY step (`chooseCustomerPurchaseOrderLineBySalesOrderPrice`, `planCustomerPurchaseOrderLineQuantityFromSalesOrders`, `applyCustomerPurchaseOrderLineQuantitiesFromSalesOrders`)
45
+ 1. Each SO line reuses a PO line with same item AND cost == SO price. Prefer the line it is already linked to, then lowest `lineNumber`.
46
+ 2. None → create a new line and add it to the lookup, so later same-item same-price SO lines in the run reuse it.
47
+ 3. Every PO line whose link was added/deleted is recomputed: qty = SUM of distinct linked SO lines (all SOs) with same item and price == cost. Recompute, not increment — a re-run gives the same result.
48
+ 4. Sum 0 → leave qty. Never below received qty (`ItemReceiptItems`) → throws.
49
+ 5. Closed SO lines still count.
50
+ 6. Dry-run → logs `SO-QTY DRY-RUN`, writes nothing for this step.
51
+
52
+ ## Decisions
53
+ - **Why NYCHH is fair game:** its customer POs are built only by our NetSuite SO import.
54
+ - **Why SO-QTY is off for Compass:** Compass customer POs are the customer's own order (exist before the SOs) and sync must never change them ([fix rule 7](../../worker/features/netsuite-togasupply-per-client-sync.md)). Also:
55
+ - Kits: one PO line (e.g. kit item 3597, cost 846.15, qty 1) links to ~6 different-item SO part lines at price 0.
56
+ - Combined POs: SO `otherrefnum` like `42019207-5125 + 42019108-5125` links each SO to both POs — summing would double qty.
57
+ - Measured 2025-09..2026-10: same-item same-price qty mismatches 0–13 lines/month (March 2026 one-off batch: 1,909).
58
+
59
+ ## Gotchas
60
+ - **Billed line cannot be deleted.** `BillItems` has no api2 route (not a Core Record; `GET /bill-items` returns `EV-7 "ACL Record could not be loaded"`, not 404), so the merge cannot see it. FK `BillItems_purchaseOrderItemId` is ON DELETE NO ACTION (verified prod `Client_Nychh`) → the delete fails loud.
61
+ - **Root cause of NYCHH duplicates was the SO import, not the TO matcher.** PO 129 (EIT0012493): `syncSalesOrderFromNetsuite` on 2026-09-08 made new cost-0 lines because SO lines had price 0 and reuse needed equal quantity.
62
+ - Prod-verified 2026-10-09: EIT0012493 lines 7–12 (cost 0, no links) deleted; 6 lines left, all 23 TO links intact. Dry run on EIT0012016: 388 lines → 31 item+cost groups (357 deletes), qty conserved.
63
+
64
+ ## Change history
65
+ - 2026-10-09 — Created: merge step (all TO clients), matcher cost rank, SO-QTY opt-in (hh only), `--dry-run`. CTO: agree with conditions, all applied. TRUE-82606. (jcardinal)
@@ -6,7 +6,7 @@ project: Library
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-10-02
9
+ updated: 2026-10-09
10
10
  owners: [jcardinal]
11
11
  files:
12
12
  - library/app/api/toga2.php
@@ -15,6 +15,7 @@ files:
15
15
  - worker/crons/toga2/netsuite/process_netsuite_order.php
16
16
  - test/@jeff/netsuite/test_match_transfer_order_purchase_order_lines.php
17
17
  related:
18
+ - customer-po-line-merge-and-quantity.md
18
19
  - ../../../2.0/apps/_underscore/features/sales-order-purchase-order-bridge-direction.md
19
20
  - ../../worker/features/netsuite-togasupply-per-client-sync.md
20
21
  - ../../worker/workflows/reprocessing-one-netsuite-order.md
@@ -46,9 +47,9 @@ Expected sets, from NetSuite only:
46
47
  Line matching — `matchTransferOrderItemsToPurchaseOrderItems()` (public static, options array; helpers `chooseReusablePurchaseOrderLine`, `chooseUnclaimedPurchaseOrderLine`, `getStockedPurchaseOrderItemUuids`):
47
48
  1. Candidates = same item `uuid`, then `partNumber` (exact — no normalization; `210-BFZY-A` ≠ `210-BFZY`).
48
49
  2. A TO line with NetSuite `createdPo` pairs only inside that PO.
49
- 3. **Upstream (customer PO, 325/329)** — rank: existing link (unless it is unstocked while a stocked line exists) > stocked line (has a `PurchaseOrderItems_SalesOrderItems` row) > not yet used by this TO > equal qty > lowest line. A PO line MAY be reused by several TO lines (soft claim). A moved link logs `RELINK` to error_log.
50
+ 3. **Upstream (customer PO, 325/329)** — rank: existing link (unless it is unstocked while a stocked line exists) > stocked line (has a `PurchaseOrderItems_SalesOrderItems` row) > cost == NetSuite TO rate > not yet used by this TO > equal qty > lowest line. A PO line MAY be reused by several TO lines (soft claim). A moved link logs `RELINK` to error_log.
50
51
  4. **Downstream (vendor PO, 326/330)** — unchanged: keep valid existing link, prefer equal qty, each PO line used once.
51
- 5. Empty/orphan PO lines are never auto-deleted.
52
+ 5. After the link sync, each upstream customer PO runs the [merge step](customer-po-line-merge-and-quantity.md) (deletes empty duplicates, merges same item + same cost).
52
53
 
53
54
  Deletes are logged: grep `reconcileTransferOrderPurchaseOrderLinks` in the worker log.
54
55
 
@@ -63,5 +64,6 @@ Deletes are logged: grep `reconcileTransferOrderPurchaseOrderLinks` in the worke
63
64
  - Stale test: `test/@Bala/tests/netsuite_transferorder_payload_tests.php` "PO number from the column" case fails since the `TransferOrders.purchaseOrderId` drop ([transfer-orders page](../../../2.0/apps/toga25-supply/features/transfer-orders-page.md)).
64
65
 
65
66
  ## Change history
67
+ - 2026-10-09 — Upstream rank adds cost == TO rate after stocked; [merge step](customer-po-line-merge-and-quantity.md) runs per upstream customer PO (empty lines now auto-deleted). TRUE-82606. (jcardinal)
66
68
  - 2026-10-02 — Upstream line matching: stocked-line ranking + reuse; new PO line only when item absent; RELINK log; downstream unchanged. CTO alternative (soft claim, upstream only) adopted. (jcardinal)
67
69
  - 2026-09-24 — Created: two-direction reconcile replaces the upstream-only linker; fixed vendor POs written upstream; 326/330 now live + ACL'd. Prod-verified. (jcardinal)
@@ -26,6 +26,7 @@ files:
26
26
  - test/@srija/Elite Testing/Service Requests/test_diagnose_togasupply_elite.php
27
27
  - dbchanges2/_modules/netsuite/2026-09-17a - StaleSalesOrderRetirementAcl.sql
28
28
  related:
29
+ - ../../library/features/customer-po-line-merge-and-quantity.md
29
30
  - ../architecture.md
30
31
  - ../../../2.0/apps/_underscore/features/shipping-carriers-and-accounts.md
31
32
  - forecast2-netsuite-reconciliation.md
@@ -1268,6 +1269,7 @@ library (or vice versa) crashes GroWrk and Adyen on their next sync run.
1268
1269
  - **Full mechanics of the stocking-flag mode** (PO bridges, origin-location resolution):
1269
1270
  [NYCHH NetSuite → TransferOrders import](../../../clients/nychh/features/netsuite-transfer-order-import.md).
1270
1271
  Legacy-rule background: [GroWrk transfer order flow](../../../clients/growrk/features/transfer-order-flow.md).
1272
+ - **`isEnabledCustomerPoLineQuantityFromSalesOrders`** (`CLIENT_CONFIGURATION`) — SO import reuses the same-item same-price customer PO line and sets qty from linked SO lines. On for **hh only**; off for Compass by design. See [merge + SO-QTY](../../library/features/customer-po-line-merge-and-quantity.md).
1271
1273
  - `isParentCustomer` — `true` walks NetSuite child customers (`listChildCustomers`); `false`
1272
1274
  syncs the single customer. Prudential-EDI and the live Quad wrapper use `false`.
1273
1275
  - The original combined-array entries used `App_Api_Toga2::CLIENT_UUID_*` consts for Quad and
@@ -2140,6 +2142,7 @@ library (or vice versa) crashes GroWrk and Adyen on their next sync run.
2140
2142
  [NetSuite Sync Alert Monitor](../../library/features/netsuite-sync-alert-monitor.md).
2141
2143
 
2142
2144
  ## Change history
2145
+ - 2026-10-09 — Customer PO duplicate lines: merge step (all TO clients) + SO-QTY opt-in (hh); [doc](../../library/features/customer-po-line-merge-and-quantity.md). Library + worker deploy together. TRUE-82606. (jcardinal)
2143
2146
  - 2026-10-09 — **Item-fulfillment tracking numbers now carry a carrier** (TRUE-82527). The fan-out passes the fulfillment’s shipping-method carrier into ; the number decides, the method is only a fallback, and no carrier row is ever created. (bala)
2144
2147
  - 2026-10-06 — Added "Fix rules" section: self-heal, repair-by-code, fail-loud, dependencies-first, no forward cursor skip, links-are-data, Compass delete boundary. (jcardinal)
2145
2148
  - 2026-10-06 — NYCHH ITEM_FULFILLMENTS frozen 09-30→10-06 (Event issue 483, FK 1451 on IA→IF line link) fixed: reverse retire TO→SO + IA-link move before every IF-line delete (library only). Corrected "NYCHH once a day" — it runs every 5 min. (jcardinal)
@@ -6,7 +6,7 @@ project: Worker
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
9
- updated: 2026-10-06
9
+ updated: 2026-10-09
10
10
  owners: ["jcardinal", "rgirish"]
11
11
  files:
12
12
  - worker/crons/toga2/netsuite/process_netsuite_order.php
@@ -16,6 +16,7 @@ files:
16
16
  - worker/crons/toga2/netsuite/common_sync_togasupply.php
17
17
  - library/app/api/netsuite/rest.php
18
18
  related:
19
+ - ../../library/features/customer-po-line-merge-and-quantity.md
19
20
  - ../features/netsuite-togasupply-per-client-sync.md
20
21
  - ../../library/features/transfer-order-purchase-order-link-reconcile.md
21
22
  - ../../../clients/nychh/features/netsuite-transfer-order-import.md
@@ -37,6 +38,8 @@ From the `worker/` directory:
37
38
  php crons/toga2/netsuite/process_netsuite_order.php <client> --tranid=<NS order #>
38
39
  php crons/toga2/netsuite/process_netsuite_order.php <client> --id=<NS internal id>
39
40
  ```
41
+ `--dry-run` (order script only) → defines `NETSUITE_PO_LINE_MERGE_DRY_RUN`: logs `MERGE DRY-RUN` / `SO-QTY DRY-RUN` and skips only those writes ([merge + SO-QTY](../../library/features/customer-po-line-merge-and-quantity.md)). **The rest of the order sync still writes.** Unknown flags exit 1.
42
+
40
43
  `<client>` = the wrapper suffix of `sync_togasupply_<client>.php` (e.g. `hh`, `growrk`, `elite`). Pass exactly one of `--tranid` / `--id`. Both accept a comma list (`--id=a,b`, `--tranid=a,b`): runs in order, stops on the first error.
41
44
 
42
45
  Steps the script runs:
@@ -47,7 +50,7 @@ Steps the script runs:
47
50
  5. Every line closed and no related records → `deleteClosedOrderFromNetsuite` ([rule](../../library/features/netsuite-closed-order-rule.md)); TO (detection mode) → `syncTransferOrderFromNetsuite` (includes the [two-direction PO link reconcile](../../library/features/transfer-order-purchase-order-link-reconcile.md)); else → `syncSalesOrderFromNetsuite`.
48
51
  6. `listPurchaseOrderIdsCreatedFromOrder()` → `syncPurchaseOrderFromNetsuite` for every vendor PO created from the order.
49
52
 
50
- Verify: grep the PHP error log for `reconcileTransferOrderPurchaseOrderLinks` and `syncPurchaseOrderFromNetsuite: DELETE`.
53
+ Verify: grep the output for `reconcileTransferOrderPurchaseOrderLinks`, `syncPurchaseOrderFromNetsuite: DELETE`, `MERGE`, `SO-QTY`, `ERROR`.
51
54
 
52
55
  ### Fulfillments / receipts: `process_netsuite_records.php`
53
56
  ```
@@ -69,11 +72,13 @@ php crons/toga2/netsuite/process_netsuite_inventory_adjustment.php <client> --id
69
72
  - `process_netsuite_item_fulfillment.php` overlaps `process_netsuite_records.php --fulfillment` (built in parallel).
70
73
 
71
74
  ## Gotchas
75
+ - **No PHP error log file on the 1.0 worker CLI** — `error_log()` goes to stderr only. Long runs: `php ... 2>&1 | tee /tmp/<name>.log`, then grep the file. Terminal scrollback trims and loses it.
72
76
  - **Convention:** every single-record `process_*` CLI tool takes `--id=<id>[,<id>...]` from the start: validate all ids up front, dedupe, run in order, stop at the first error and report how many were done.
73
77
  - A TO skipped by the sync (out-of-scope customer) prints `NOT imported` — read the error log, not a script bug.
74
78
  - Needs `IS_ENABLED_INTEGRATION_TRANSFER_ORDERS` true in the wrapper for TO routing; otherwise the order syncs as a sales order.
75
79
 
76
80
  ## Change history
81
+ - 2026-10-09 — `process_netsuite_order.php --dry-run` (merge + SO-QTY writes only); unknown flags exit 1; log via `2>&1 | tee`. (jcardinal)
77
82
  - 2026-10-02 — `process_netsuite_order.php` accepts comma lists for `--id` / `--tranid` (`a72f17f5`). (jcardinal)
78
83
  - 2026-09-30 — Added `process_netsuite_item_fulfillment.php` + `process_netsuite_inventory_adjustment.php`. (jcardinal)
79
84
  - 2026-09-30 — Added `process_netsuite_records.php` (fulfillments/receipts by id); Closed routing follows the closed-order rule (v3: line-level). (rgirish)
@@ -4,7 +4,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
4
4
 
5
5
  ## 1.0 framework
6
6
 
7
- - **library** (Library) _(framework core)_ — 35 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
7
+ - **library** (Library) _(framework core)_ — 36 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
8
8
  - **worker** (Worker) — 42 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
9
9
  - **dbchanges** (Database Changes) _(framework core)_ — 1 doc(s) → [1.0/apps/dbchanges/INDEX.md](1.0/apps/dbchanges/INDEX.md)
10
10
  - **worker1.5** (Worker 1.5) — 0 doc(s) → [1.0/apps/worker1.5/INDEX.md](1.0/apps/worker1.5/INDEX.md)
@@ -11,6 +11,7 @@ apps:
11
11
  - toga-blox
12
12
  - toga2-commerce
13
13
  - worker
14
+ - library
14
15
  - worker1.5
15
16
  - worker2
16
17
  - dbchanges2
@@ -22,7 +23,7 @@ project: _Underscore
22
23
  client: compass-usa
23
24
  type: profile
24
25
  status: active
25
- updated: 2026-10-08
26
+ updated: 2026-10-09
26
27
  owners: [jcardinal, bala, tcox, apeterson, dfranks, akhokhani, ajean, rgirish]
27
28
  files: []
28
29
  related:
@@ -19,7 +19,7 @@ project: _Underscore
19
19
  client: nychh
20
20
  type: profile
21
21
  status: active
22
- updated: 2026-10-08
22
+ updated: 2026-10-09
23
23
  owners: [jcardinal, apeterson, bala, akhokhani]
24
24
  files:
25
25
  - dbchanges2/Client_Nychh/2026-09-02a - TransferOrderNetsuitePushInterceptor.sql
@@ -73,6 +73,7 @@ TOGA 2.0 client on `_underscore`, prod schema `Client_Nychh`. Supply-chain / ful
73
73
  - **Prod NetSuite feeds (2026-10-02):** only ITEM_FULFILLMENTS + INVENTORY_ADJUSTMENTS run (worker `511a7fd9`, 2026-09-30), cursors rewound to 2022/2023.
74
74
  - **TO destination + own ship-to (2026-10-02, library `8f974d93`, _underscore `6839dd3e`).** NetSuite-entered TOs landed on catch-all Location 2 (text match failed on Queens hyphens + multi-address sites). Destination now = mapped NetSuite address-book entry → text match → Location 2; TOs own `shipToAddressId`; Location auto-create uses default shipping. Mapping: 61 entries → 18 sites. See [TO import](./features/netsuite-transfer-order-import.md), [address-book mapping](../../1.0/apps/worker/workflows/netsuite-addressbook-location-mapping.md).
75
75
  - **TO↔customer-PO line matching fixed (2026-10-02, `1bc4de52`).** Old matcher picked unstocked duplicate PO lines and created empty PO lines; now ranks stocked lines and allows reuse. 161 TOs re-processed; 86 bad links over 62 TOs remained at last measure (re-process result pending). See [reconcile](../../1.0/apps/library/features/transfer-order-purchase-order-link-reconcile.md).
76
+ - **Customer PO duplicate lines (2026-10-09, TRUE-82606).** Cause: SO import made new cost-0 lines (reuse needed equal qty). Fix: merge step + SO-QTY (`isEnabledCustomerPoLineQuantityFromSalesOrders`, on for hh only). See [merge + SO-QTY](../../1.0/apps/library/features/customer-po-line-merge-and-quantity.md).
76
77
  - 🚩 **Two `Countries` rows share `c_netsuiteCountry = '_unitedStates'`** (id 1 US; id 2 stray `_t`, NY only, ~38k addresses). Froze ITEM_FULFILLMENTS 2026-10-02 (issue 935); hotfixed by `findStateUuidForNetsuiteAddress()` fallback. Stray `_t` cleanup OPEN.
77
78
  - **All 24 shipping locations shared ONE address until 2026-09-09 — the head-office Accounts Payable bill-to (50 Water St).** Each location had its own `Addresses` row (261-303) but they held identical values, and locations 1 + 2 shared `Addresses.id = 1`. `Client_Nychh/2026-09-09c - UpdateLocationShippingAddresses.sql` backfilled each from its NetSuite customer's `defaultshipping` address (applied + verified in prod: 22 distinct addresses over 24 locations). It only ever showed on the **transfer-order** screens, because `TransferOrders` has no ship-to column while `SalesOrders` carries its own `shipToAddressId`. Catch-all **location 2 "NYC Health + Hospitals"** now at 55 Water St 10041; its big TO count was mostly an import overwrite bug (fix 2026-09-24, see [NetSuite TO import](./features/netsuite-transfer-order-import.md)), and Bellevue/Jacobi carry NetSuite customer ids (29275 / 31917) that no longer exist. See [location shipping addresses](./features/location-shipping-addresses.md).
78
79
  - 🚨 **sandbox-client (`Core` + `Client_Nychh`) was reset from PRODUCTION again on 2026-09-04** — second reset in two days (first, 2026-09-03, over the stale `purchase-orders` TableView). Every "verified on <date>" sandbox-client claim in this profile dated **before 2026-09-04 now describes whatever prod had**. The 2026-09-01 → 2026-09-03 `dbchanges2` migrations may need re-running on sandbox-client. Re-read the environment before trusting any sandbox state; procedure + re-run order in [Targeting the right database](../../2.0/apps/dbchanges2/workflows/targeting-the-right-database.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.989",
3
+ "version": "1.0.991",
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",
@@ -500,20 +500,16 @@ Draft a short comment for a non-technical project manager (also used for release
500
500
 
501
501
  ```
502
502
  **What changed**
503
-
504
503
  - <plain, PM-friendly point>
505
504
  - <...>
506
505
 
507
506
  **Manual steps**
508
-
509
507
  - <plain point>
510
508
 
511
509
  **Still open**
512
-
513
510
  - <plain point>
514
511
 
515
512
  **Ticket fields changed by Claude**
516
-
517
513
  - <plain sentence with the reason>
518
514
  ```
519
515
 
@@ -531,7 +527,8 @@ Rules:
531
527
  Application because the work also changed the Desk app."
532
528
  6. **No Apps, Clients, or PRs sections.** Apps and clients are in the ticket fields; PRs link
533
529
  to the ticket through the GitHub integration.
534
- 7. **One blank line between sections**, and between a heading and its bullets.
530
+ 7. **One blank line between sections only.** No blank line between a heading and its bullets —
531
+ the first bullet goes on the line right under the heading.
535
532
 
536
533
  Do not ask the developer to approve it. Write the text to a temp file
537
534
  (`toga-comment-<TICKET>.md` in the OS temp dir), run the **Secret scan** on it, delete the