toga-ai 1.0.291 → 1.0.292
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.
|
@@ -92,6 +92,15 @@ project-local `_Component_*`/`_Model_*` must still declare the project namespace
|
|
|
92
92
|
unqualified `catch (Exception $e)` fails **silently** — the more insidious of the two.
|
|
93
93
|
|
|
94
94
|
## Change history
|
|
95
|
+
- 2026-07-08 — **Production recurrence** of this exact fatal (concrete fingerprint, no doc change
|
|
96
|
+
to the mechanism): a worker2 deploy declared `_Component_Forecast_SaleImport`
|
|
97
|
+
(`Worker/Netsuite/Invoice.php` autoload) and its sibling `_Component_Forecast_Db`
|
|
98
|
+
(`Component/Forecast/Db/Db.php`, opportunity jobs) in the global namespace → every
|
|
99
|
+
Forecast-writing NetSuite webhook type threw at autoload the moment the deploy landed, flipping
|
|
100
|
+
a Forecast-wide ~98%-success window to ~72%-failure at a single timestamp. Confirms the
|
|
101
|
+
invisible-to-`php -l`, blocks-all-writes-before-DB behavior in prod. Diagnostic path (how the
|
|
102
|
+
before/after `isSuccess` split is read out of `Core.WorkerJobs`) is captured in the
|
|
103
|
+
[Forecast.Sales import doc's diagnostic playbook](./forecast-sale-import.md). (dfranks)
|
|
95
104
|
- 2026-07-08 — Documented the `_Component_*`/`_Model_*` project-namespace registration
|
|
96
105
|
requirement (Loader.php eval-bridge mechanism, ~71-83), the runtime-only failure mode (not
|
|
97
106
|
caught by `php -l`), and the three constructs that must be backslash-qualified inside a
|
|
@@ -447,6 +447,46 @@ Keep **create→delete windows short** so most records slip between cron runs; o
|
|
|
447
447
|
`import_sales` during a batch. (The daily discrepancy-fix also cleans stragglers since the
|
|
448
448
|
record is deleted in NetSuite.)
|
|
449
449
|
|
|
450
|
+
## Diagnostic playbook — proving whether inbound webhooks actually wrote Forecast.Sales (run cold)
|
|
451
|
+
Durable procedure for answering "did the real-time webhook path work, or is a `fixer.php`
|
|
452
|
+
sweep masking a broken pipeline?" It spans **two DB clusters** — correlate them; never infer
|
|
453
|
+
success from a `Forecast.Sales` row alone.
|
|
454
|
+
1. **Receipt (prod-logs `Logs.Webhook`)** — proves the POST *arrived*, nothing more. Columns:
|
|
455
|
+
`id, uuid, dtStamp, sourceIp, method, route, requestPayload` (JSON with
|
|
456
|
+
`recordType`/`internalId`/`eventType`/`action`). NetSuite hits land on route `/netsuite`.
|
|
457
|
+
**There is NO status/error column** — a row here means "received", not "succeeded".
|
|
458
|
+
2. **Success/failure (prod-core `Core.WorkerJobs`)** — the authoritative outcome:
|
|
459
|
+
`isSuccess` (NULL=pending/running, 0=fail, 1=success), `failureReason` (mediumtext, full
|
|
460
|
+
stack trace), `action`, `dtCreated`.
|
|
461
|
+
3. **Two-stage fan-out.** The router job `action='Netsuite/Webhook'` fans out to per-record-type
|
|
462
|
+
**child** jobs (`Netsuite/Invoice/post`, `Netsuite/SalesOrder/put`, `Netsuite/Opportunity/*`,
|
|
463
|
+
etc.). The **router almost always succeeds**; the Forecast writes — and their failures — live
|
|
464
|
+
in the **child** jobs. Filter WorkerJobs on the child `action`, not the router, to judge the
|
|
465
|
+
pipeline.
|
|
466
|
+
4. **CRITICAL — a `Forecast.Sales` row does NOT prove the webhook worked.** `fixer.php` (the
|
|
467
|
+
reconciliation sweep) independently writes/repairs rows, so a present row can mask a fully
|
|
468
|
+
broken webhook path. **Always correlate `Logs.Webhook.dtStamp` against
|
|
469
|
+
`WorkerJobs.isSuccess`/`dtCreated` by uuid/timestamp** — never conclude success from row
|
|
470
|
+
presence.
|
|
471
|
+
5. **Bucket errors by class.** `failureReason` embeds a per-row `[uuid] ` prefix; strip it with
|
|
472
|
+
`CASE`/`REGEXP_REPLACE` to collapse per-row UUIDs into error classes and get clean counts —
|
|
473
|
+
the fast way to distinguish a deployment-wide fatal from long-tail noise.
|
|
474
|
+
|
|
475
|
+
**Known WorkerJobs failure classes on this pipeline (durable):**
|
|
476
|
+
- **Autoload fatal blocks ALL Forecast writes before any DB work** — a global-namespace
|
|
477
|
+
`_Component_Forecast_SaleImport`/`_Component_Forecast_Db` throws at autoload (see the
|
|
478
|
+
[namespace-registration doc](./component-model-namespace-registration.md)); every
|
|
479
|
+
Forecast-writing child type (Invoice/SalesOrder/Opportunity/JournalEntry/CashSale/CreditMemo)
|
|
480
|
+
fails identically and the fatal is **invisible to `php -l`**. A clean before/after `isSuccess`
|
|
481
|
+
split at a single deploy timestamp is the fingerprint (a bad push flips a Forecast-wide
|
|
482
|
+
~98%-success window to ~72%-failure). Recurred in production 2026-07-08.
|
|
483
|
+
- **`Netsuite/InventoryItem/*` / `NonInventoryItem/post` — `watchdog: exceeded maxExecutionTime
|
|
484
|
+
without completing`**: item syncs timing out at the 300s watchdog.
|
|
485
|
+
- **`Netsuite/Invoice/post` — MySQL error 1213 `Deadlock found when trying to get lock`** on the
|
|
486
|
+
Forecast DB: pre-existing, low-volume, ~1.6–3.9s each.
|
|
487
|
+
- **`Netsuite/Webhook` router — `Unsupported NetSuite eventType: approve`** (`Worker/Netsuite.php`
|
|
488
|
+
~line 53): salesOrder `approve` events aren't handled by the router and fail at fan-out.
|
|
489
|
+
|
|
450
490
|
## Gotchas / known issues
|
|
451
491
|
- **Testing locally pollutes PROD unless you UNDEPLOY the prod AMQ enqueuer first.** Each sale
|
|
452
492
|
transaction type has **TWO** NetSuite UE enqueuer deployments: **"AMQ — Enqueuer"**
|
|
@@ -499,6 +539,19 @@ record is deleted in NetSuite.)
|
|
|
499
539
|
- The cron's sign handling is not portable here — see Sign convention.
|
|
500
540
|
|
|
501
541
|
## Change history
|
|
542
|
+
- 2026-07-08 — **Added the webhook-vs-fixer diagnostic playbook** for proving whether inbound
|
|
543
|
+
NetSuite webhooks actually wrote `Forecast.Sales`: correlate `Logs.Webhook` (prod-logs;
|
|
544
|
+
receipt-only, NO status column) against `Core.WorkerJobs` (prod-core; `isSuccess`/
|
|
545
|
+
`failureReason`), judge the **child** `Netsuite/<Type>/<verb>` jobs (not the almost-always-OK
|
|
546
|
+
`Netsuite/Webhook` router), and **never infer success from a `Forecast.Sales` row** because
|
|
547
|
+
`fixer.php` independently writes/masks rows — correlate timestamps. Bucket `failureReason` with
|
|
548
|
+
`CASE`/`REGEXP_REPLACE` (strip the `[uuid] ` prefix) into error classes. Recorded a concrete
|
|
549
|
+
**production recurrence (2026-07-08)** of the global-namespace autoload fatal on
|
|
550
|
+
`_Component_Forecast_SaleImport`/`_Component_Forecast_Db` (Forecast-wide, invisible to `php -l`,
|
|
551
|
+
clean before/after `isSuccess` split at a single deploy timestamp), plus three lower-volume
|
|
552
|
+
WorkerJobs failure classes on this pipeline (item-sync watchdog timeout; Invoice deadlock 1213;
|
|
553
|
+
router `Unsupported NetSuite eventType: approve`). Read-only diagnostic session, no code change.
|
|
554
|
+
(dfranks)
|
|
502
555
|
- 2026-07-08 — **Clarified the revenue sign convention is NOT the `amountDue` "store raw
|
|
503
556
|
positive" rule.** The TRUE-78923 "don't sign-flip credit memos/refunds" guidance applies
|
|
504
557
|
only to `amountDue` (which this importer does not write); the line `amount`/revenue sign
|
package/package.json
CHANGED