toga-ai 1.0.292 → 1.0.293
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.
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
| [Carrier Shipping Labels (UPS/FedEx) & NetSuite Item Fulfillment](features/carrier-shipping-labels.md) | Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label (UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracki | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ItemFulfillments/TrackingNumber.php, _underscore/Component/Library/LabelPdf/LabelPdf.php, _underscore/Component/Library/Carriers/ShipmentRequest/ShipmentRequest.php, _underscore/Component/Library/Carriers/Ups/Ups.php, _underscore/Component/Library/Carriers/Fedex/Fedex.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Trait/Netsuite/SalesOrder.php, _underscore/Component/Library/NetSuite/NetSuite.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ShippingMethod.php, _underscore/Model.php, _underscore/Cloud.php |
|
|
11
11
|
| [_Component_*/_Model_* project-namespace registration (autoloader) & backslash-qualify traps](features/component-model-namespace-registration.md) | Every **project-local** `_Component_*` and `_Model_*` class in a 2.0 app **must declare the project namespace** at the top of the file: ```php namespace <NAMESP | _underscore/Loader.php, worker2/_.php, api2/_.php, worker2/Component/Forecast/Db/Db.php, worker2/Component/Forecast/SaleImport/SaleImport.php, api2/Component/Api/Netsuite/Netsuite.php |
|
|
12
12
|
| [Client Email Template Sending](features/email-template-sending.md) | `_Model_Client_EmailTemplate` sends a stored, client-defined email template by UUID. | _underscore/Model/Client/EmailTemplate.php, _underscore/Model/Client/EmailTemplateOutgoingEmailAddress.php, _underscore/Email.php |
|
|
13
|
-
| [Error Reporting — Issue/Event Aggregation (
|
|
13
|
+
| [Error Reporting — Issue/Event Aggregation (agreed POST-to-receiver design)](features/error-reporting-issue-event.md) | Platform-wide error-reporting infrastructure for TOGA 2.0, built around a two-table **Issue / Event** aggregation model in the shared **Core Logs DB**. | _underscore/Error.php, _underscore/Model/Core/Logs/Issue.php, _underscore/Model/Core/Logs/Event.php, dbchanges2/Logs/2026-07-06 - Issue and Event tables.sql |
|
|
14
14
|
| [Record-Changed Event Publishing (_Event::publish to SQS)](features/event-publish-sqs.md) | `_Event::publish()` (in `_underscore/Event.php`) is the PHP side of the real-time event pipeline. | _underscore/Event.php |
|
|
15
15
|
| [Forecast.Sales NetSuite import engine (real-time webhook)](features/forecast-sale-import.md) | Real-time importer that takes a NetSuite **sale** record and writes its lines into `Forecast.Sales` (the Forecast2 revenue table). | _underscore/Component/Forecast/SaleImport/SaleImport.php, _underscore/Component/Forecast/Db/Db.php, _underscore/Component/Api/Netsuite/Netsuite.php, worker2/Worker/Netsuite/Invoice.php, worker2/Worker/Netsuite/CashSale.php, worker2/Worker/Netsuite/CreditMemo.php, worker2/Worker/Netsuite/CashRefund.php, worker2/Worker/Netsuite/JournalEntry.php, worker2/Worker/Netsuite/Opportunity.php, worker2/Worker/Netsuite/SalesOrder.php, dbchanges2/Forecast/2026-06-26a - Add journalEntry to Sales transaction type enum.sql, test/@dave/test_invoice_lifecycle.php, test/@dave/test_je_lifecycle.php, test/@dave/test_creditmemo_lifecycle.php, test/@dave/test_cashsale_lifecycle.php, test/@dave/test_cashrefund_lifecycle.php, test/@dave/test_fetchrecord_routes.php, test/@dave/verify_je_classification.php, test/@dave/probe_je_accounts.php, test/@dave/probe_je_shape.php, test/@dave/fixer.php, test/@dave/Junk Drawer/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/Junk Drawer/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js |
|
|
16
16
|
| [Item-Fulfillment Stage Lifecycle (picked/packed/shipped) & Order Status](features/item-fulfillment-stage-lifecycle-and-order-status.md) | Every ItemFulfillment (IF) now carries an explicit **stage** — picked → packed → shipped — resolved through `ItemFulfillmentStages → ItemFulfillmentStatuses` (m | _underscore/Model/Client/SalesOrder.php, _underscore/Model/Quad/SalesOrder.php, _underscore/Model/Compass/SalesOrder.php, _underscore/Model/Compass/SalesOrderStatus.php, _underscore/Model/Client/SalesOrderItem.php, _underscore/Model/Client/Item.php, _underscore/Model/Client/PurchaseOrderItem.php, library/app/api/toga2.php, dbchanges2/Client/2026-06-30a - BackfillNullStageItemFulfillmentsToShipped.sql, dbchanges2/Client/2026-06-30b - SalesOrderStatusesPickedPacked.sql, dbchanges2/Client/2026-06-30c - ItemFulfillmentStageIdNotNull.sql, dbchanges2/Client_CompassCanada/2026-06-30a - ItemFulfillmentLifecycleAndShippedBackfill.sql |
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Error Reporting — Issue/Event Aggregation (
|
|
2
|
+
title: Error Reporting — Issue/Event Aggregation (agreed POST-to-receiver design)
|
|
3
3
|
framework: "2.0"
|
|
4
4
|
repo: _underscore
|
|
5
5
|
project: _Underscore
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-08
|
|
10
10
|
owners: ["dfranks"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Error.php
|
|
@@ -21,39 +21,51 @@ related:
|
|
|
21
21
|
|
|
22
22
|
## Summary
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
Platform-wide error-reporting infrastructure for TOGA 2.0, built around a two-table
|
|
25
|
+
**Issue / Event** aggregation model in the shared **Core Logs DB**. An **Issue** is the
|
|
26
|
+
deduplicated record for a distinct error (keyed by a hash of the stack trace) carrying an
|
|
27
|
+
aggregate `occurrences` counter and an escalatable `urgency`; an **Event** is one row per
|
|
28
|
+
individual occurrence with its own trace and context.
|
|
29
|
+
|
|
30
|
+
**The agreed architecture (see below) decouples *reporting* from *persistence*:**
|
|
31
|
+
application error handlers **POST** error payloads to a centralized `/errors` receiver
|
|
32
|
+
endpoint; a **worker2 cron** consumes them and does all the hashing, upserting, aggregation,
|
|
33
|
+
escalation, and ClickUp sync into the Core Logs DB. Handlers do **not** write Issue/Event
|
|
34
|
+
rows to the database directly. This is the canonical target design for the whole
|
|
35
|
+
error-monitoring epic (TRUE-781xx) — the handler ticket, the dashboard ticket, and the
|
|
36
|
+
escalation-cron ticket must all conform to it.
|
|
37
|
+
|
|
38
|
+
## Agreed error-monitoring architecture
|
|
39
|
+
|
|
40
|
+
Source: the **"2026-05-21 - Errors, Monitoring and Alerts"** meeting design (approved;
|
|
41
|
+
cited via Talos DevCore meeting notes). Pipeline:
|
|
42
|
+
|
|
43
|
+
1. **Report (application side).** An application's error/exception handler serializes the
|
|
44
|
+
error (message, stack trace, context) and **POSTs it to a centralized `/errors` receiver
|
|
45
|
+
endpoint** (the meeting referenced `webhook.hub.com/error`). The handler's only job is to
|
|
46
|
+
transmit — it performs **no direct database persistence**.
|
|
47
|
+
2. **Ingest (worker2 cron).** A worker2 cron consumes the received error payloads and:
|
|
48
|
+
- builds `issueHash` from the stack trace to identify the distinct error,
|
|
49
|
+
- **upserts an Issue** and **inserts an Event** into the **Core** Logs DB,
|
|
50
|
+
- aggregates occurrence counts within a time window,
|
|
51
|
+
- auto-escalates / de-escalates the Issue `urgency` based on occurrence frequency,
|
|
52
|
+
- syncs the Issue to ClickUp.
|
|
53
|
+
3. **Scope.** API 2.0 handlers first; 1.0 is deferred.
|
|
54
|
+
|
|
55
|
+
The critical rule: **handlers POST; the cron persists (to Core).** Any approach where the
|
|
56
|
+
handler writes Issue/Event rows inline to a database contradicts this design.
|
|
57
|
+
|
|
58
|
+
## Data model (Core Logs DB)
|
|
59
|
+
|
|
39
60
|
- **`_underscore/Model/Core/Logs/Issue.php`** — `const DATABASE = _underscore::DB_LOGS;
|
|
40
61
|
const TABLE = 'Issues';`. Fields: `id, uuid, dtCreated, dtLastOccurred, urgency, subject,
|
|
41
|
-
description, clickupIdentifier, hash, errorMessage, occurrences, trace`.
|
|
62
|
+
description, clickupIdentifier, hash, errorMessage, occurrences, trace`. One row per
|
|
63
|
+
distinct error (dedup key = `hash`); `occurrences` is the rolling count, `dtLastOccurred`
|
|
64
|
+
the most-recent hit, `urgency` a list field, `clickupIdentifier` links to an escalation
|
|
65
|
+
ticket. There is **no `type` column**.
|
|
42
66
|
- **`_underscore/Model/Core/Logs/Event.php`** — `const DATABASE = _underscore::DB_LOGS;
|
|
43
67
|
const TABLE = 'Events';`. Fields: `id, uuid, issueId (FK → Issue), errorMessage, trace,
|
|
44
|
-
context, dtCreated`.
|
|
45
|
-
|
|
46
|
-
## How it works
|
|
47
|
-
|
|
48
|
-
1. On an uncaught exception, hash the backtrace to identify the distinct error.
|
|
49
|
-
2. Load the existing `Issue` by `hash`. On a miss, create it with `errorMessage`, `trace`,
|
|
50
|
-
`urgency = 'LOW'`, and `occurrences = 0`.
|
|
51
|
-
3. **Increment `occurrences`** (`($issue->occurrences ?? 0) + 1`) and set
|
|
52
|
-
`dtLastOccurred`, then save — so the counter advances on every occurrence, hit or new.
|
|
53
|
-
4. Insert one `Event` for this occurrence: `issueId`, `errorMessage`, per-occurrence
|
|
54
|
-
`trace`, and `context` (`print_r($GLOBALS, true)`).
|
|
55
|
-
5. Commit the `DB_LOGS` transaction; surface `Error {issueId}, Event {eventId}` in the
|
|
56
|
-
debug body.
|
|
68
|
+
context, dtCreated`. One row per occurrence.
|
|
57
69
|
|
|
58
70
|
The `Issues`/`Events` tables live in the shared **Core Logs** DB, provisioned from
|
|
59
71
|
`dbchanges2/Logs/` (the `Logs/` folder targets the framework-level, non-tenant `Logs` DB
|
|
@@ -61,35 +73,55 @@ with unqualified table names — see the dbchanges2 architecture folder→DB map
|
|
|
61
73
|
with per-client API/error logging in `Logs_<Tenant>` via `Model/Client/Logs/*`
|
|
62
74
|
(`DB_CLIENT_LOGS`, `dbchanges2/Logs_Client/`).
|
|
63
75
|
|
|
64
|
-
##
|
|
76
|
+
## Current state of `_underscore/Error.php` (important — reads differently than you'd expect)
|
|
77
|
+
|
|
78
|
+
`_underscore/Error.php` registers `exceptionHandler()` via `set_exception_handler`, but in
|
|
79
|
+
**`_production` it does NOT persist any Issue/Event rows.** It builds a debug HTML body,
|
|
80
|
+
writes it to an S3 error-details file, and (when a Logs register is present) records a
|
|
81
|
+
single `_Model_Client_Logs_Error` / `_Model_Core_Logs_Error` row — the older flat error log.
|
|
82
|
+
There is no Issue/Event aggregation, no `issueHash` upsert, and no ClickUp escalation in the
|
|
83
|
+
handler on `_production`.
|
|
65
84
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
**An inline-DB-persist approach exists on an in-flight branch, but it conflicts with the
|
|
86
|
+
agreed design and must not be treated as the model to follow:** that approach has the
|
|
87
|
+
handler compute `$issueHash = md5(serialize($backtrace))` and directly upsert
|
|
88
|
+
`_Model_Client_Logs_Issue` + insert `_Model_Client_Logs_Event`, committing a transaction —
|
|
89
|
+
i.e. handler-side direct persistence, and to the **Client** Logs DB rather than **Core**.
|
|
90
|
+
That contradicts the approved architecture on two counts: (a) handlers should POST to the
|
|
91
|
+
`/errors` receiver, not persist; and (b) aggregation belongs in the Core Logs DB, driven by
|
|
92
|
+
the worker2 cron. New work (TRUE-78178 handlers) should implement the POST-to-receiver path,
|
|
93
|
+
not extend the inline-persist branch.
|
|
71
94
|
|
|
72
95
|
## Gotchas / known issues
|
|
73
96
|
|
|
74
|
-
-
|
|
75
|
-
|
|
97
|
+
- **Do not assume error persistence "already works."** On `_production` the handler writes
|
|
98
|
+
only an S3 debug body and a flat `*_Logs_Error` row — no Issue/Event aggregation exists in
|
|
99
|
+
production yet. The Issue/Event model is the *target*, delivered by the POST→cron pipeline.
|
|
100
|
+
- **`occurrences` must be incremented, not just seeded** — a naive upsert that sets
|
|
101
|
+
`occurrences = 0` only at creation never advances the aggregate counter. The cron must
|
|
76
102
|
increment on every occurrence.
|
|
77
|
-
- **`Event.trace` must be
|
|
78
|
-
|
|
79
|
-
- **Persistence
|
|
80
|
-
`\_Database::$_registers[_underscore::DB_LOGS]` isn't set,
|
|
81
|
-
skipped
|
|
103
|
+
- **`Event.trace` must be populated per occurrence** — the per-Event `trace` column is easy
|
|
104
|
+
to leave empty; record it on every Event.
|
|
105
|
+
- **Persistence must be conditional on the Core-Logs register.** If the Core Logs register
|
|
106
|
+
(`\_Database::$_registers[_underscore::DB_LOGS]`) isn't set, an Issue/Event write must be
|
|
107
|
+
skipped rather than fatal — but this belongs in the cron, not the handler.
|
|
108
|
+
- **Client vs Core Logs DB mix-up.** The Issue/Event tables belong in the **Core** Logs DB
|
|
109
|
+
(`DB_LOGS`, `Model/Core/Logs/*`). Writing them to a per-client Logs DB
|
|
110
|
+
(`DB_CLIENT_LOGS`, `_Model_Client_Logs_*`) is the wrong target and does not match the
|
|
111
|
+
agreed design.
|
|
82
112
|
- **Shared-schema migration collision.** Because `Issues`/`Events` live in the *shared* Core
|
|
83
|
-
Logs DB,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
`IssueEmailAddresses` table, one adding this error-class persistence). Only **one**
|
|
87
|
-
migration may create the shared tables; reconcile overlapping `Logs/` create-scripts at
|
|
88
|
-
merge so the tables are created exactly once and later migrations only `ALTER`.
|
|
113
|
+
Logs DB, two dbchanges2 `Logs/` migrations that both `CREATE TABLE Issues`/`Events` will
|
|
114
|
+
collide ("table already exists"). Only **one** migration may create the shared tables;
|
|
115
|
+
later migrations only `ALTER`.
|
|
89
116
|
|
|
90
117
|
## Change history
|
|
91
118
|
|
|
92
|
-
- 2026-07-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
119
|
+
- 2026-07-08 — Corrected KB drift: documented the approved 2026-05-21 error-monitoring
|
|
120
|
+
architecture (handlers POST to a centralized `/errors` receiver; a worker2 cron builds
|
|
121
|
+
issueHash, upserts Issues + inserts Events into the Core Logs DB, aggregates/escalates,
|
|
122
|
+
syncs ClickUp — handlers do not persist). Clarified that `_production` Error.php performs
|
|
123
|
+
no Issue/Event persistence (only an S3 debug body + flat `*_Logs_Error` row), and that the
|
|
124
|
+
inline-DB-persist approach (handler-side upsert to the *Client* Logs DB) conflicts with the
|
|
125
|
+
agreed design and is not the pattern to follow. (dfranks)
|
|
126
|
+
- 2026-07-06 — Prior doc described the exceptionHandler as directly persisting Issue/Event
|
|
127
|
+
rows to the Core Logs DB with dedup; superseded by the 2026-07-08 correction above. (dfranks)
|
package/package.json
CHANGED