toga-ai 1.0.1110 → 1.0.1111
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: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
9
|
+
updated: 2026-10-22
|
|
10
10
|
owners: [sking, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- dbchanges/index.php
|
|
@@ -36,7 +36,7 @@ Two rules dominate everything below:
|
|
|
36
36
|
1. Confirm the target schema/environment (`Core/` = legacy V1 `Core`).
|
|
37
37
|
2. Branch per the git-workflow rules (`fix/...`, `feature/...`) — never commit to `_production`.
|
|
38
38
|
3. Write **one file, commentary first, statements last**, no trailing comment chunk, no explicit `COMMIT`, and no `DELIMITER`/stored-routine constructs.
|
|
39
|
-
4. Wrap any risky data change in the derived-table preflight guard (below) so a drifted before-state is a no-op rather than a wrong write.
|
|
39
|
+
4. Wrap any risky data change in the derived-table preflight guard (below) so a drifted before-state is a no-op rather than a wrong write. One-time repairs also follow *Rules for a one-time data repair*.
|
|
40
40
|
5. Open a PR into `_production` (or the target `_<env>` branch).
|
|
41
41
|
6. After merge: `_<env>` branches apply within ~2 min; **`_production` waits for the next worker deploy** — verify with a read-only query afterward rather than assuming. **Merging the PR applies nothing by itself** — `dbchanges` has no CI, no workflow, no runner of its own; every application is driven from the worker tier.
|
|
42
42
|
7. If the change spans more than one top-level (database) folder, verify **each database separately** — see the silent-skip trap below.
|
|
@@ -84,6 +84,14 @@ WHERE t.id IN (<ids>)
|
|
|
84
84
|
) AS preflight) = <expected-row-count>;
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
### Rules for a one-time data repair (TRUE-83064, dbchanges#270)
|
|
88
|
+
|
|
89
|
+
1. **A multi-direction move is ONE `UPDATE`** — `SET col = CASE id WHEN … THEN … END` over the whole id set, under one preflight that requires the **exact before-state count of all rows**. Two UPDATEs can half-apply; one statement is all-or-nothing.
|
|
90
|
+
2. **Guard a dependent follow-up UPDATE on the previous step's after-state** (its preflight counts rows in the state step 1 should have left). If step 1 no-op'd, step 2 no-ops too.
|
|
91
|
+
3. **Limit the forward UPDATE to the exact id list** (never a broad `WHERE` like "all units on item X"). The rollback file uses the **same ids** and pins the **current** (after) values.
|
|
92
|
+
4. **Never raise an SQL error on guard mismatch** (no forced-error tricks). The runner `exit()`s on any error and blocks every file queued after it. A mismatch must leave the file a quiet no-op.
|
|
93
|
+
5. **A no-op still commits the `_dbchanges` row**, so the file never re-runs. Re-read the data after apply; if it no-op'd, ship a new file.
|
|
94
|
+
|
|
87
95
|
### One top-level folder = one DATABASE (and the silent-skip trap)
|
|
88
96
|
|
|
89
97
|
The repo's top level is a list of **database names**, not modules: `Core`, `TOGaDeskSupport`, `Common`, `Logs`, `Bridge_NetSuite`, … The runner walks every top-level directory and treats the **folder name as the database to connect to** (`index.php` ~L55–60), then — for each folder — scans `config.<env>.ini` and uses the **first `[database*]` group it finds** (~L76–80; in `config.prod.ini` that is `[database_main]`) for hostname/username/password. So *all* folders are attempted with **one** set of credentials against one host, with only the DB name varying.
|
|
@@ -116,6 +124,7 @@ The repo ships `config.alpha/beta/demo/hotfix/prod/stage/test/worker.ini`, there
|
|
|
116
124
|
- **⚠ SECURITY — hardcoded GitHub token.** `worker/crons/infrastructure/execute_dbchanges.php` contains a **plaintext GitHub personal access token** in the small config block near the top of the file (~L14–18), and interpolates it into a `shell_exec()` git-clone command line — which also exposes it in the worker host's process listing. Treat the token as **compromised**: rotate it, move it to config/env (`config.<env>.ini`), and pass it without embedding it in an argv string. No credential value is recorded here by design.
|
|
117
125
|
|
|
118
126
|
## Change history
|
|
127
|
+
- 2026-10-22 — TRUE-83064 (dbchanges#270, CodeRabbit review): one-time data-repair rules — one CASE UPDATE with exact-count preflight, chained after-state guards, exact-id forward + pinned rollback, no SQL error on mismatch, no-op still commits `_dbchanges`. (sking)
|
|
119
128
|
- 2026-10-19 — Comment lines ending in `;` also split the file; verify SELECTs in the header without `;` (TRUE-82935) (jcardinal)
|
|
120
129
|
|
|
121
130
|
## Related
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: nycdoe
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
9
|
+
updated: 2026-10-22
|
|
10
10
|
owners: [mhammontree, sking, bala]
|
|
11
11
|
files:
|
|
12
12
|
- worker/crons/sync/nycdoe/import_asn.php
|
|
@@ -249,6 +249,12 @@ All **live** DOE business logic is 1.0. The only other 2.0 code is the in-progre
|
|
|
249
249
|
Note this only confirms the row is an eligible *candidate* for the `*/5 min` Stage 5 cron (which then picks ONE ASN at random via `ORDER BY RAND() LIMIT 1` across all pending ASNs) — it does not by itself confirm a ticket was created; wait for the cron and check `togadeskRepairOrderId`/`dtInstallTicketStatusChanged` to confirm ticketing completed.
|
|
250
250
|
- Unlike instances 1–2 below, no duplicate/replacement SO was involved here — the mismatch is a same-ASN sibling PO the warehouse had already been using for other lines on this ASN.
|
|
251
251
|
|
|
252
|
+
- **⚠ OPEN BUG (TRUE-83064, 2026-10-22; data repaired, root-cause code fix NOT done) — one customer PO split into two ASNs (two vendors) → wrong manufacturer + stranded cross-received units.** Customer PO `WR270089579` came in as two ASNs, one under the Acer vendor and one under the Lexmark vendor, for the same Acer part `DOE-NX.JHWAA.004-NYCDOE` (likely the Acer-as-Lexmark SFTP path, Stage 1b above).
|
|
253
|
+
- **Manufacturer comes from the ASN vendor, not the part.** Stage 5 (`3_create_installation_ticket.php`) sets the TOGa Desk manufacturer from the ASN's `Vendors.name` and finds the MSO by `advanceShippingNoticeId`. Two ASNs = two MSOs, and the Lexmark-ASN line was ticketed as Lexmark.
|
|
254
|
+
- **Cross-received serials dead-end silently.** Stage 5 matches an item's units only against receipts of that item's **own** SO. The warehouse received serials onto the **other** PO of the same customer PO, so they never match, and the customer-PO-scoped duplicate-serial guard refuses to add them as new units. No error; the cron retries forever. Evidence: legacy `Logs.API` `getItemReceipt` rows with `sourceJob LIKE '%3_create_installation_ticket%'` (30-day retention).
|
|
255
|
+
- **Repair (data-only, dbchanges#270, branch `fix/repair-wr270089579-consolidate`):** `Core/SK/2026-10-22-data-repair-wr270089579-consolidate.sql` swaps the 102+102 cross-received `AdvanceShippingNoticeUnits` onto the item whose SO received them and moves item `55578` onto ASN `27180`; `TOGaDeskSupport/SK/2026-10-22-data-repair-wr270089579-consolidate.sql` moves the 24 Lexmark ROs onto MSO `334826` as Acer (assets → manufacturer `10` / model `23668`) and deletes the empty MSO `334820`. Two databases — verify each. Guard rules: [dbchanges workflow](../../../1.0/apps/dbchanges/workflows/authoring-and-shipping-sql-files.md) → *Rules for a one-time data repair*.
|
|
256
|
+
- **⚠ OUTSTANDING:** TOGa Desk Lexmark model `23782` still holds **152 mislabeled assets** on other POs (`WR270055266` MSO `332014`, `S202652963` MSO `332217`) — not fixed. Any customer PO split across vendors can repeat both failures until Stage 5 is fixed.
|
|
257
|
+
|
|
252
258
|
- **⚠ OPEN BUG (investigated 2026-06-19, not yet fixed) — manufacturer PO-suffix format variance causes DUPLICATE fulfillment.** Stage 2's serialized dedupe is scoped to the *exact* PO string (`lookupAsnUnitIdByAsnPartNumberAndSerialNumber[vendorId][purchaseOrder][part][serial]`, `legacy_process_asn_queue.php:272`), and the PO flows **verbatim** end to end — never trimmed/normalized except Lenovo's `trim()` in `asnprocessor/lenovo.php`. So when a manufacturer re-sends the *same physical unit* under a different suffix spelling (e.g. Lenovo `WR260150152-RPL` then `WR260150152-RPLE`, same serial `V60095WK`), the two PO strings differ → the serial guard misses → a full duplicate ASN→SO→MSO→RO chain is created for one device (two NetSuite SOs, two repair orders, two onsite tickets).
|
|
253
259
|
- **Confirmed live case:** PO `WR260150152`, serial `V60095WK`, asset `DOE-LN1265478` → repair orders **772308** (MSO 309909, via `-RPL`) and **801064** (MSO 318945, via `-RPLE`), both `ORDER_ASSIGNED_AWAITING_SCHEDULING` (uncaught as of investigation), same TOGa Desk `serialNumId 592519`.
|
|
254
260
|
- **Systemic, not a one-off:** ~14 same-base/same-serial duplicate pairs across **Lenovo AND Lexmark**, spanning suffix forms `-RPL`/`-REPL`/`-RPLE`/`-N`/`_N`. A genuine replacement carries a *new* serial, so identical-serial-across-base-and-suffix = true duplication.
|
|
@@ -288,6 +294,7 @@ All **live** DOE business logic is 1.0. The only other 2.0 code is the in-progre
|
|
|
288
294
|
- Local deep-dive (full WR260236464 case study with record IDs): `doe_service_now_integration.md` in Mark's WWW workspace root; also the `service-now-integration` skill (`asn-import-flow.md`)
|
|
289
295
|
|
|
290
296
|
## Change history
|
|
297
|
+
- 2026-10-22 — TRUE-83064: repaired customer PO `WR270089579` (two ASNs, Acer + Lexmark vendor) via dbchanges#270; open-bug gotcha — Stage 5 takes the manufacturer from the ASN vendor and keys the MSO on the ASN; cross-received serials dead-end silently; 152 mislabeled assets on Lexmark model `23782` remain. No code change. (sking)
|
|
291
298
|
- 2026-10-19 — Gated delete live since ~10-11 and its re-release login storm (worker2 SFTP IP ban); file names matter to 1.0 only for FileLog/dedupeKey (rename = re-email); open Lexmark 1969-12-31 date bug. (bala)
|
|
292
299
|
- 2026-10-16 — TRUE-82776: link to the update-rejection monitor; parked RITM1320328 repair-order loop and POD blank-ticket 400s. (mhammontree)
|
|
293
300
|
- 2026-10-14 — TRUE-82735: RITM + POD outbound crons revived (per-MSO loop, own-RITM match, state ownership, POD ordering/isolation), library RITM filter + location lookup fixed, backlog stamps, months-long outage gotcha. Not deployed. (mhammontree)
|
package/package.json
CHANGED