toga-ai 1.0.1102 → 1.0.1104

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: Library
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-10-09
9
+ updated: 2026-10-21
10
10
  owners: [dfranks, jcardinal, kyalamarthi, rgirish, bala]
11
11
  files:
12
12
  - library/app/api/netsuite/rest.php
@@ -68,6 +68,7 @@ Field semantics (verified on the live account):
68
68
  - **SO originating line** lives in `previoustransactionlinelink` (correlated subquery on `previoustype='SalesOrd'`), because `transactionline` has **no `orderline` column**.
69
69
  - **TO_DATE rejects impossible dates** (e.g. `2026-06-31`) with an opaque HTTP 400 — validate calendar dates before building SuiteQL.
70
70
  - **⚠ SuiteQL returns date/datetime columns in the account DISPLAY format (here `M/D/YYYY`, date-only) unless you wrap them in `TO_CHAR` — losing the time is silent (2026-09-02).** A bare `SELECT lastmodifieddate` came back `9/2/2026` (no time, locale-dependent order), while `TO_CHAR(lastmodifieddate,'YYYY-MM-DD HH24:MI:SS')` gave `2026-09-02 17:39:21`. Any shim that **stores or compares a NetSuite date STRING** must `TO_CHAR(col,'YYYY-MM-DD HH24:MI:SS')` it, or it drops the time and inherits the account's locale format. This defeated the per-client sync's per-record resume cursor: it stored `9/2/2026|<id>`, sat at midnight, and re-scanned the whole day every run — fixed by `TO_CHAR`-ing `lastmodifieddate AS lastmodifieddate` in the 8 cursor-feeding SELECTs (`listSalesOrders`, `fetchSalesOrderByIdFull`, `listSales`, `listPurchaseOrders`, `fetchPurchaseOrderById`, `listItemReceipts`, `listItemFulfillments`, `listInventoryAdjustments`). `WHERE`/`ORDER BY` are unaffected by the alias — bound via `TO_TIMESTAMP` — so they still use the base column. **Still latent:** `listOpportunities` and `listItems` return raw date-only `lastmodifieddate` into their shims; harmless only because neither feeds a resume cursor yet. Engine-side detail on the [per-client-sync doc](../../worker/features/netsuite-togasupply-per-client-sync.md).
71
+ - **⚠ A list-type custom field shimmed with `nameCol => null` stores the list ID ("2"), not the name — `shimInventoryAssignment` did this for serials (fixed 2026-10-21, TRUE-82674).** `custitemnumber_charging_brick` / `_charging_cable` / `_device_status` / `_grade` were defined with `nameCol` null, so the shim emitted the plain internal id and Units `c_chargingBrick` / `c_chargingCable` / `c_deviceStatus` / `c_grade` saved "2" instead of "NO". **Fix:** `BUILTIN.DF(...) AS <field>_df` added to all 3 `inventoryassignment` SuiteQL queries (inventory adjustment, item fulfillment, item receipt) and set as `nameCol`. **Rule: any list/select custom field needs a `BUILTIN.DF` column in the SELECT and a `nameCol`; plain text/date fields keep `nameCol => null`.** Lists: `customlist462` YES=1 NO=2 N/A=4 Unknown=5 (brick + cable), `customlist_device_status`, `customlist_grade` A=1..D=4 F=5. Elite's 113 bad units fixed by `dbchanges2/Client_Elite/2026-10-21b - UnitListIdsToNames.sql` ([Elite export](../../../clients/elite/features/supply-csv-export.md)). **Open:** other NetSuite clients' Units may hold the same ids — not checked. All TOGa Supply clients use **one** NetSuite account (`[netsuite]` config), so every `custitemnumber_*` field exists for every client.
71
72
  - **The line SuiteQL must SELECT the line location or it is always NULL — the shim reads it but didn't select it.** `listSalesOrders()`'s line query now selects `tl.location` and `BUILTIN.DF(tl.location) AS location_name`. The line-shim assembly code already *read* those two fields, but the SELECT never included them, so every line's location came back null — which broke NYCHH transfer-order origin resolution (`TransferOrders.originLocationId` is NOT NULL → V2 `EV-10`). Same class of bug as ShipItem cost above. (Added 2026-08-27.)
72
73
  - **Custom body fields ride in `customFieldList` on the per-id REST GET.** `fetchSalesOrderById()` now carries `custbody_stocking_order` (NetSuite internal id 7097) via `customFieldList` so the importer can route NYCHH orders (checked = stocking/in → SalesOrders, unchecked = transfer/out → TransferOrders). A custom field absent from the record reads as null downstream. See [NYCHH NetSuite → TransferOrders import](../../../clients/nychh/features/netsuite-transfer-order-import.md).
73
74
  - **⚠ `scriptId` in a `customFieldList` def is CASE-SENSITIVE on lookup — a case skew silently returns the default (2026-08-29).** `custbody_stocking_order` was **tagged** in the shim with the uppercase scriptId `CUSTBODY_STOCKING_ORDER` but **read** by the importer as lowercase `custbody_stocking_order`, so the lookup never matched and always fell back to the default (unchecked) — which mis-routed **every** NYCHH order to a Transfer Order. Fixed by lowercasing the scriptId in **both** `buildSalesOrderShims()` and `fetchSalesOrderById()` defs; `END_CUSTOMER` is intentionally left uppercase because it is resolved by internalId `3149`, not by scriptId. Nothing errors on a case skew — the field just reads as its default — so verify the exact scriptId string on both the write and read side. See [NYCHH TransferOrders import](../../../clients/nychh/features/netsuite-transfer-order-import.md).
@@ -98,6 +99,7 @@ Field semantics (verified on the live account):
98
99
  - **`App_NetSuite::getCustomFieldValue()` (`library/app/netsuite.php`) must be warning-safe.** 1.0 runs on PHP 7.2 in an unguarded path, so any PHP warning → `exit()` (kills the cron). It was hardened 2026-08-27 with `?? []` / `?? null` guards on the custom-field-list traversal. Keep that discipline when touching it.
99
100
 
100
101
  ## Change history
102
+ - 2026-10-21 — `shimInventoryAssignment`: list fields (charging brick/cable, device status, grade) now carry `BUILTIN.DF` names in the 3 `inventoryassignment` queries; they stored list ids before. TRUE-82674 (rgirish)
101
103
  - 2026-10-09 — `fetchItemFulfillmentById` now exposes `shipMethod` (internalId + name), because `transaction.shipcarrier` is the useless literal `FedEx/USPS/More` on every row and `shipitem` has no carrier column. It feeds the 1.0 carrier resolver. (bala)
102
104
  - 2026-09-21 — `listSales()` gained a `$recordTypes` allowlist param (defaults to all 4 for the forecast import); callers pass `RECORD_TYPE_*` constants, not raw SuiteQL codes; throws on unknown/empty. Lets the togasupply sync request INVOICES only. (jcardinal)
103
105
 
@@ -6,7 +6,7 @@
6
6
  | [Elite — NetSuite → TOGa Supply inbound sync (TRUE-80499 onboarding)](features/netsuite-togasupply-sync.md) | 1.0 | Elite is the 18th client on the shared NetSuite → TOGa Supply importer ([engine](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md)). |
7
7
  | [Elite SalesOrder → NetSuite Push (postPost/postPut interceptors → worker2)](features/salesorder-netsuite-push.md) | 2.0 | Elite's event-driven SalesOrder → NetSuite push (postPost/postPut interceptors → worker2); open for the push trigger, interceptor registration, or the 2026-08-2 |
8
8
  | [Elite — Sales Order stage change posts a reply on the TOGa Desk (1.0) ticket](features/salesorder-status-togadesk-reply.md) | 2.0 | How an Elite sales-order stage change posts a reply back onto the originating TOGa Desk (1.0) ticket; open for the cross-DB join path or the dedup key. |
9
- | [Elite: Service Requests + Inventory CSV export (TOGa 2.5 Supply)](features/supply-csv-export.md) | 2.0 | Elite-only Export buttons on the toga25-supply Service Requests and Inventory pages: what each exports and why Inventory ignores on-screen filters; open before |
9
+ | [Elite: Service Requests + Inventory CSV export (TOGa 2.5 Supply)](features/supply-csv-export.md) | 2.0 | Elite-only Export buttons on the toga25-supply Service Requests and Inventory pages: what each exports, the Units column order, and which CSV columns are not in |
10
10
  | [Elite — supply2 frontend scope (Inventory + Service Requests, both built)](features/supply2-scope.md) | 2.0 | Elite's toga2-supply frontend (host `ELITE`): the Inventory + Service Requests pages, how each is host-scoped, and the shared-component fixes made along the way |
11
11
  | [Elite — Supply TableView config (stale joins, Inventory row filters, Serial Number search)](features/supply2-tableview-config-drift.md) | 2.0 | Elite's `Client_Elite` TOGa Supply table views: dead-id join drift, which view Inventory reads, its row filters, and column search; open before any Elite TableV |
12
12
  | [Elite — raising a Service Request from a TOGa Desk ticket (App_Api_ServiceRequest)](features/togadesk-service-request-intake.md) | 1.0 | How an Elite agent raises a Service Request from a TOGa Desk (1.0) ticket (the SR chain's front door, logic in `App_Api_ServiceRequest`); open for the button ga |
@@ -6,34 +6,51 @@ project: TOGa 2.5 Supply
6
6
  client: elite
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-10-16
9
+ updated: 2026-10-21
10
10
  owners: ["rgirish"]
11
11
  files:
12
12
  - toga25-supply/src/pages/ServiceRequests/ServiceRequests.tsx
13
13
  - toga25-supply/src/pages/Inventory/Inventory.tsx
14
14
  - toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx
15
+ - dbchanges2/Client_Elite/2026-10-21a - InventoryUnitsCsvColumns.sql
16
+ - dbchanges2/Client_Elite/2026-10-21b - UnitListIdsToNames.sql
15
17
  related:
16
18
  - ../profile.md
19
+ - ./supply2-tableview-config-drift.md
17
20
  - ../../../2.0/apps/toga25-supply/features/tableview-csv-export.md
21
+ - ../../../1.0/apps/library/features/netsuite-suiteql-rest-shim.md
18
22
  - ../../../2.0/apps/toga25-supply/features/record-modals-and-nested-tables.md
19
23
  - ../../prudential/features/supply-orders-csv-export.md
20
24
  - ../../../2.0/apps/toga25-supply/architecture.md
21
25
  ---
22
- Elite-only Export buttons on the toga25-supply Service Requests and Inventory pages: what each exports and why Inventory ignores on-screen filters; open before changing either button.
26
+ Elite-only Export buttons on the toga25-supply Service Requests and Inventory pages: what each exports, the Units column order, and which CSV columns are not in the DB; open before changing either button or the Units view.
23
27
 
24
28
  ## Summary
25
- Elite gets an **Export** button on *Service Requests* and *Inventory* (TRUE-82674, commit `b8201b5`). Both use the shared [TableView CSV export](../../../2.0/apps/toga25-supply/features/tableview-csv-export.md). Prod sizes 2026-10-16: `Client_Elite` Units 733, ServiceRequests 3.
29
+ Elite gets an **Export** button on *Service Requests* and *Inventory* (TRUE-82674). Both use the shared [TableView CSV export](../../../2.0/apps/toga25-supply/features/tableview-csv-export.md). Prod sizes 2026-10-21: `Client_Elite` Units 746, ServiceRequests 3.
26
30
 
27
31
  ## How it works
28
- - **Service Requests** exports the `service-requests` TableView exactly as shown, all pages: the page's display meta, URL searchParams, and the active filter-button `additionalData` (now returned by `useSalesOrdersViewModel`).
29
- - **Inventory** exports **every row of the deepest level of the active grouping**. Elite's grouping is prod `Client_Elite.SurfaceOverrides` element 118 `CONFIG`: root `inventory_items` → leaf `inventory_units` (`additionalDataSlug` Items). So the file is all units. Meta is fetched on click (see the shared doc).
30
- - **Gate:** `clientSlug === "ELITE"` in the page, marked `TODO(surface)`. Same interim pattern as the [Prudential export](../../prudential/features/supply-orders-csv-export.md) on `SalesOrders.tsx`.
32
+ - **Service Requests** exports the `service-requests` TableView exactly as shown, all pages: the page's display meta, URL searchParams, and the active filter-button `additionalData` (returned by `useSalesOrdersViewModel`).
33
+ - **Inventory** always exports the **`units` grouping** (deepest level), whatever grouping is on screen; falls back to the active grouping's deepest level if no `units` grouping exists. For Elite that is `inventory_units` (`Client_Elite.TableViews` id 21) via SurfaceOverride element 120 — not the Core default `units-for-items-for-purchase-orders` (id 17). Always check the client override, not Core ([config doc](./supply2-tableview-config-drift.md)). Meta is fetched on click.
34
+ - **Columns = the view's visible fields in meta order.** So the CSV layout is set in `TableViewFields`, not in code. `2026-10-21a` orders `inventory_units` to follow Elite's serial CSV: Part # (= CSV Item), Unit Description (`c_itemDescription`), Serial, Asset Tag, Unit Condition, Grade, Charging Brick, Charging Cable, Last Modified Date (`c_lastModified`), Device Status, then the rest.
35
+ - **Gate:** `clientSlug === "ELITE"` in the page, marked `TODO(surface)`. Same interim pattern as the [Prudential export](../../prudential/features/supply-orders-csv-export.md).
31
36
 
32
37
  ## Decisions
33
- - **Inventory export ignores on-screen filters and sort (all units).** Root filters are keyed `inventory_items_*` and never reach the units query. Every expanded nested units table writes sort/filter under the shared `inventory_units` slug, so passing URL params would silently narrow the whole export. Open question: label the button "Export all units".
38
+ - **Inventory export ignores on-screen filters and sort (all units).** Root filters are keyed `inventory_items_*` and never reach the units query. Every expanded nested units table writes sort/filter under the shared `inventory_units` slug, so passing URL params would silently narrow the whole export.
39
+ - **Always the `units` grouping** (CodeRabbit, PR #69): the file must have the same columns whatever grouping the user is on.
40
+ - **No new Units columns (2026-10-21, rgirish).** Export only data already in the DB. CSV columns with no DB source are left out until decided one by one (see Gotchas).
34
41
 
35
42
  ## Gotchas
36
- - **Open follow-up (not done):** move the gate to Surface. CTO suggestion: an `inventory.export` element on the `inventory-list-actions` surface, Core seeded hidden + an Elite `SurfaceOverride`, like `inventory.createTransferOrder`.
43
+ - **"Wipe Status" was really Device Status.** The 2026-08-07 seed labelled the `c_deviceStatus` column "Wipe Status". Fixed in `2026-10-21a` (slug `deviceStatus`, label "Device Status"). Real wipe status is a different NetSuite field (below).
44
+ - **Grade / Charging Brick / Charging Cable / Device Status held NetSuite list ids ("2"), not names**, from the 1.0 REST sync bug ([shim doc](../../../1.0/apps/library/features/netsuite-suiteql-rest-shim.md)). 113 Elite units fixed by `2026-10-21b` (maps ids to names, re-runnable).
45
+ - **Open — CSV columns Elite wants that are NOT in the DB** (developer reviewing each; all are NetSuite inventory-number fields):
46
+ - Date Received/Inspected — `custitemnumber_last_recieved_date_serial` (date)
47
+ - Notes — `inventorynumber.memo`
48
+ - Wipe Status — `custitemnumber_wiped` (list)
49
+ - Lock Status — `custitemnumber_lock_status` (list)
50
+ - Return Tracking — `custitemnumber_return_tracking_number` (text). The numbers exist in `Client_Elite.TrackingNumbers` but are not linked to units.
51
+ - Condition — `Units.unitConditionId` is empty for all 746 prod units; no NetSuite serial field feeds it.
52
+ - **Open follow-up:** move the gate to Surface. CTO suggestion: an `inventory.export` element on the `inventory-list-actions` surface, Core seeded hidden + an Elite `SurfaceOverride`, like `inventory.createTransferOrder`.
37
53
 
38
54
  ## Change history
55
+ - 2026-10-21 — Inventory export always uses the `units` grouping; Units columns reordered to Elite's CSV, "Wipe Status" relabelled Device Status, list ids mapped to names; missing CSV columns listed. TRUE-82674 (rgirish)
39
56
  - 2026-10-16 — Created: Service Requests + Inventory export for Elite, TRUE-82674 (rgirish)
@@ -6,7 +6,7 @@ project: Database Changes
6
6
  client: elite
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-10-14
9
+ updated: 2026-10-21
10
10
  owners: [tcox, bala, rgirish]
11
11
  files:
12
12
  - dbchanges2/Client_Elite/
@@ -16,6 +16,7 @@ files:
16
16
  - dbchanges2/Client_Elite/2026-09-15a - InventoryHideServiceClassItems.sql
17
17
  - dbchanges2/Client_Elite/2026-10-05e - InventoryUnitsOnlyInWarehouse.sql
18
18
  - dbchanges2/Client_Elite/2026-10-14a - InventoryUnitsSerialNumberFilter.sql
19
+ - dbchanges2/Client_Elite/2026-10-21a - InventoryUnitsCsvColumns.sql
19
20
  - dbchanges2/Core/2026-08-07 - ServiceRequests TableView.sql
20
21
  - dbchanges2/Client/2026-08-11a - ServiceRequestsInventoryUnitsTableViews.sql
21
22
  - dbchanges2/Client_Nychh/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql
@@ -151,8 +152,10 @@ hand-insert, so the next tenant refresh keeps it.
151
152
  default labels. **Elite side:** 2 `SalesOrderStatuses` (shipped, delivered), 2
152
153
  `AclFieldPermissions`, 1 `AclActionPermissions`, 6 label overrides.
153
154
 
154
- **Elite's 4 custom `inventory_units` columns** (indexes 14-17: `grade`, `chargingBrick`,
155
- `chargingCable`, `wipeStatus` via `c_deviceStatus`) plus their
155
+ **Elite's custom `inventory_units` columns** (`grade`, `chargingBrick`, `chargingCable`,
156
+ `deviceStatus` — seeded 2026-08-07 as slug `wipeStatus` / label "Wipe Status" but reading
157
+ `c_deviceStatus`, renamed by `2026-10-21a`; plus `itemDescription`, `lastModified` from `2026-10-21a`;
158
+ column order: [Elite CSV export](./supply-csv-export.md)) plus their
156
159
  `ClientCustomRecordFieldSettings` labels live in the **Elite** file, not the all-clients one -
157
160
  a `customRecordFieldId` names a *different field per client*, so it can never go in `Client/`.
158
161
 
@@ -435,6 +438,8 @@ the old `SVC-` prefix check back into the clause.
435
438
  [error-reporting-issue-event](../../../2.0/apps/_underscore/features/error-reporting-issue-event.md).
436
439
 
437
440
  ## Change history
441
+ - 2026-10-21 — `inventory_units` columns reordered to Elite's serial CSV, mislabelled "Wipe Status"
442
+ column renamed `deviceStatus` (`2026-10-21a`, TRUE-82674). (rgirish)
438
443
  - 2026-10-14 — Serial Number column search on `inventory_units` (`2026-10-14a`, `isFilterable`
439
444
  only, no frontend change; Asset Tag left off). Recorded that the view lists only owned-warehouse
440
445
  units (`2026-10-05e`), so serial search misses shipped units. Corrected the stale "still in
@@ -6,8 +6,8 @@ project: Claude Harness
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
9
- updated: 2026-10-15
10
- owners: ["jcardinal"]
9
+ updated: 2026-10-21
10
+ owners: ["jcardinal", "rgirish"]
11
11
  files:
12
12
  - claude/skills/start/SKILL.md
13
13
  - claude/skills/commit/SKILL.md
@@ -126,6 +126,19 @@ All tested 2026-10-08:
126
126
  only from `~/.claude/projects/*/tool-results/`. Debug: env `TOGA_START_GATE_DEBUG=1` writes
127
127
  `gate-debug.log` in the sessions dir. Only the `ticket-reader` agent does the full read. Drop_down values come back
128
128
  as an orderindex — map them through `type_config.options`.
129
+ - **macOS: `/start` fails with "Write to temp dir blocked by start-gate hook (ticket not
130
+ verified yet)"** (seen 2026-10-21, TRUE-82674). A small ticket comes back inline (not saved
131
+ to `tool-results/`), so the `ticket-reader` must Write it to a temp file (step 4). The gate
132
+ allows that Write only under `~/.claude` or `os.tmpdir()`/`$TEMP`/`$TMP`
133
+ (`safeZonePrefixes()`). On macOS that is `$TMPDIR` (`/var/folders/.../T/`); the reader picked
134
+ `/tmp` (= `/private/tmp`) and was blocked. Windows is fine (`%TEMP%` matches).
135
+ - Workaround: re-run the `ticket-reader` and tell it to save inline results under
136
+ `~/.claude/toga-ticket-tmp/` (not `toga-sessions` or any `tool-results` folder). Auto mode
137
+ may block the retry as a gate bypass — leave auto mode and OK it.
138
+ - Real fix (not done yet): `ticket-reader` step 4 names the folder (`$TMPDIR`, never `/tmp`,
139
+ or `~/.claude/toga-ticket-tmp/`), or `safeZonePrefixes()` also allows `/tmp` and `/private/tmp`.
140
+ - The reader must not trim the raw task JSON before parsing. Verify still passes when it
141
+ does (it checks id/url against the hook's `seen` read), so this goes unnoticed.
129
142
  - **Done status is `development finished`** (lowercase; valid on the Dev Sprints lists).
130
143
  - **The ClickUp GitHub integration works:** a PR whose title starts with the ticket id auto-links.
131
144
  worker2 also reads the first token of the PR title as the ticket id.
@@ -161,6 +174,7 @@ All tested 2026-10-08:
161
174
 
162
175
  ## Change history
163
176
 
177
+ - 2026-10-21 — TRUE-82674: macOS `/start` temp-dir block gotcha + workaround. (rgirish)
164
178
  - 2026-10-15 — Ticket writes need no approval: `/finish` writes fields itself, `/pseudocode`
165
179
  writes once the plan is agreed; the gate allows any field/status/tag/comment on the session
166
180
  ticket, except Has Code Changes. (jcardinal)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.1102",
3
+ "version": "1.0.1104",
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",