toga-ai 1.0.149 → 1.0.150

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.
@@ -2,5 +2,5 @@
2
2
 
3
3
  | Doc | Framework | Summary | Files |
4
4
  |-----|-----------|---------|-------|
5
- | [NYCDOE ServiceNow / ASN Integration](features/servicenow-integration.md) | 1.0 | The NYCDOE/ServiceNow integration mirrors DOE's ServiceNow tickets (Incidents + RITMs) into local tables, turns vendor shipment notices into NetSuite Sales Orde | worker/crons/sync/nycdoe/import_asn.php, worker/crons/sync/nycdoe/import_inc.php, worker/crons/sync/nycdoe/legacy_import_asn.php, worker/crons/sync/nycdoe/legacy_process_asn_queue.php, worker/crons/sync/nycdoe/process_tickets.php, worker/crons/sync/nycdoe/1_send_asn_to_netsuite.php, worker/crons/sync/nycdoe/2_send_serials_to_netsuite.php, worker/crons/sync/nycdoe/3_create_installation_ticket.php, worker/crons/sync/nycdoe/send_ticket_updates.php, worker/crons/sync/nycdoe/send_request_item_updates.php, worker/crons/sync/nycdoe/send_nycdoe_proof_of_delivery.php, worker/crons/sync/nycdoe/sync_nycdoe_locations.php, worker/crons/sync/nycdoe/receive_edi_purchase_orders.php, worker/crons/sync/nycdoe/send_edi_open_invoices.php, worker/crons/notifications/nycdoe/, worker/schedules/cron.worker.sync.json, worker/schedules/cron.worker.notification.json, library/app/api/nycdoe.php, library/app/api/nycdoev2.php, library/app/asnprocessor/manufacturer.php, library/app/edi.php |
5
+ | [NYCDOE ServiceNow / ASN Integration](features/servicenow-integration.md) | 1.0 | The NYCDOE/ServiceNow integration mirrors DOE's ServiceNow tickets (Incidents + RITMs) into local tables, turns vendor shipment notices into NetSuite Sales Orde | worker/crons/sync/nycdoe/import_asn.php, worker/crons/sync/nycdoe/import_inc.php, worker/crons/sync/nycdoe/legacy_import_asn.php, worker/crons/sync/nycdoe/legacy_process_asn_queue.php, worker/crons/sync/nycdoe/process_tickets.php, worker/crons/sync/nycdoe/1_send_asn_to_netsuite.php, worker/crons/sync/nycdoe/2_send_serials_to_netsuite.php, worker/crons/sync/nycdoe/3_create_installation_ticket.php, worker/crons/sync/nycdoe/send_ticket_updates.php, worker/crons/sync/nycdoe/send_request_item_updates.php, worker/crons/sync/nycdoe/send_nycdoe_proof_of_delivery.php, worker/crons/sync/nycdoe/sync_nycdoe_locations.php, worker/crons/sync/nycdoe/receive_edi_purchase_orders.php, worker/crons/sync/nycdoe/send_edi_open_invoices.php, worker/crons/notifications/nycdoe/, worker/schedules/cron.worker.sync.json, worker/schedules/cron.worker.notification.json, library/app/api/nycdoe.php, library/app/api/nycdoev2.php, library/app/asnprocessor/manufacturer.php, library/app/asnprocessor/apple.php, library/app/asnprocessor/lenovo.php, library/app/asnprocessor/lexmark.php, library/app/asnprocessor/acer.php, library/app/edi.php |
6
6
  | [New York City Department of Education](profile.md) | 1.0 | NYC DOE (New York City Department of Education) is a TOGA client whose entire integration runs in the **1.0 worker tier** (~30 cron scripts under `worker/crons/ | |
@@ -6,7 +6,7 @@ project: Worker
6
6
  client: nycdoe
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-06-10
9
+ updated: 2026-06-19
10
10
  owners: [mhammontree]
11
11
  files:
12
12
  - worker/crons/sync/nycdoe/import_asn.php
@@ -29,6 +29,10 @@ files:
29
29
  - library/app/api/nycdoe.php
30
30
  - library/app/api/nycdoev2.php
31
31
  - library/app/asnprocessor/manufacturer.php
32
+ - library/app/asnprocessor/apple.php
33
+ - library/app/asnprocessor/lenovo.php
34
+ - library/app/asnprocessor/lexmark.php
35
+ - library/app/asnprocessor/acer.php
32
36
  - library/app/edi.php
33
37
  related:
34
38
  - ../profile.md
@@ -147,10 +151,49 @@ Vendor SFTP ───(legacy_import_asn.php, ser+non-ser)─┘ [UNIQUE ded
147
151
  4. **The NetSuite freeze:** `netSuiteInternalSalesOrderId IS NOT NULL` ⇒ `qtyOrder` locked.
148
152
  Growth = new item row; decrease = discrepancy alert, never a reduction.
149
153
  5. **One unit per non-serialized item row** — a 50-cable line is 1 unit and 1 repair order.
150
- 6. **The `-RPL` PO suffix** forces a brand-new ASN/MSO/RO chain for replacements.
154
+ 6. **The replacement PO suffix forces a brand-new ASN/MSO/RO chain** for replacements —
155
+ but the suffix is a **per-manufacturer convention with NO standard format**, and even a
156
+ single manufacturer is inconsistent. Observed in prod: `-RPL`, `-REPL`, `-RPLE`, `-N`,
157
+ `_N` (underscore), and a space variant (` RPL`). ServiceNow itself documents NO `-RPL`
158
+ convention — its native replacement model is field-based on the *original* RITM
159
+ (`device_replaced`/`replaced_serial_number`/`replaced_asset_tag`); the suffix comes from
160
+ the OEM/SFTP feeds. Replacement-of-replacement appears as `-RPL-RPL` and is structurally
161
+ fine **only if hyphenated consistently**. See the duplicate-fulfillment gotcha below.
151
162
 
152
163
  ## Gotchas / known issues
153
164
 
165
+ - **⚠ OPEN BUG (investigated 2026-06-19, not yet fixed) — manufacturer PO-suffix format
166
+ variance causes DUPLICATE fulfillment.** Stage 2's serialized dedupe is scoped to the
167
+ *exact* PO string (`lookupAsnUnitIdByAsnPartNumberAndSerialNumber[vendorId][purchaseOrder]
168
+ [part][serial]`, `legacy_process_asn_queue.php:272`), and the PO flows **verbatim** end to
169
+ end — never trimmed/normalized except Lenovo's `trim()` in `asnprocessor/lenovo.php`. So when
170
+ a manufacturer re-sends the *same physical unit* under a different suffix spelling
171
+ (e.g. Lenovo `WR260150152-RPL` then `WR260150152-RPLE`, same serial `V60095WK`), the two PO
172
+ strings differ → the serial guard misses → a full duplicate ASN→SO→MSO→RO chain is created
173
+ for one device (two NetSuite SOs, two repair orders, two onsite tickets).
174
+ - **Confirmed live case:** PO `WR260150152`, serial `V60095WK`, asset `DOE-LN1265478` →
175
+ repair orders **772308** (MSO 309909, via `-RPL`) and **801064** (MSO 318945, via `-RPLE`),
176
+ both `ORDER_ASSIGNED_AWAITING_SCHEDULING` (uncaught as of investigation), same TOGa Desk
177
+ `serialNumId 592519`.
178
+ - **Systemic, not a one-off:** ~14 same-base/same-serial duplicate pairs across **Lenovo AND
179
+ Lexmark**, spanning suffix forms `-RPL`/`-REPL`/`-RPLE`/`-N`/`_N`. A genuine replacement
180
+ carries a *new* serial, so identical-serial-across-base-and-suffix = true duplication.
181
+ - **The discriminator that would have caught it:** serial + assetTag + trackingNumber were
182
+ identical across copies. A serial-anchored dedupe (`vendorId + partNumber + serialNumber`,
183
+ ignoring PO suffix) collapses these; PO-scoped dedupe cannot.
184
+ - **Do NOT blindly canonicalize all suffixes to one token** — `-RPL-RPL` (legit
185
+ replacement-of-replacement) must stay distinct from `-RPL`. Confirm suffix semantics with
186
+ the SME before any fix.
187
+ - **Two ADJACENT anomalies surfaced by the same blast-radius query** (separate mechanisms,
188
+ not yet investigated): (a) a large Lexmark cluster pairing a **numeric order number with a
189
+ WR PO** for identical serials (e.g. `3100157`↔`WR240178018`) — same devices under two PO
190
+ identifiers; (b) **invalid/placeholder serials** becoming serialized units — serial `"NA"`
191
+ spans 25 POs→15 ROs (Lenovo `NA`→`''` normalization at `asnprocessor/lenovo.php:102` not
192
+ catching all), plus `"1"`, `"11"–"29"`.
193
+ - **Open thread for resume:** check RO active/cancelled status across the ~14 suffix-variance
194
+ pairs to size live double-truck-roll exposure; then trace the numeric-PO↔WR cluster.
195
+ Investigation was **read-only** — no code or data changes made.
196
+
154
197
  - **The queue `dedupeKey` format is load-bearing.** A 2026 incident (WR260236464 duplicate
155
198
  repair order) was caused by dedupeKey format drift (4-part keys with serial vs 3-part
156
199
  without) letting old rows re-import; Stage 2 then created a sibling item row (frozen
@@ -187,6 +230,7 @@ Vendor SFTP ───(legacy_import_asn.php, ser+non-ser)─┘ [UNIQUE ded
187
230
  of the consumer query; `php -l` every touched file.
188
231
 
189
232
  ## Change history
233
+ - 2026-06-19 — Investigated (read-only) the replacement-PO-suffix duplicate-fulfillment bug: PO-scoped serial dedupe defeated by per-manufacturer suffix-format variance (`-RPL`/`-REPL`/`-RPLE`/`-N`/`_N`); confirmed live case WR260150152/V60095WK (ROs 772308+801064) and ~14 systemic pairs across Lenovo+Lexmark. Refined business rule #6; added OPEN-BUG gotcha + adjacent anomalies (numeric-PO↔WR cluster, "NA"/integer serials). No code changes. (mhammontree)
190
234
  - 2026-06-10 — Documented the NYC DOE ServiceNow/ASN integration (ticket mirror, dual ASN ingestion, NetSuite freeze pipeline, EDI surface, dedupeKey gotchas). (mhammontree)
191
235
 
192
236
  ## Related docs
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.149",
3
+ "version": "1.0.150",
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",