toga-ai 1.0.267 → 1.0.268

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.
@@ -7,6 +7,7 @@
7
7
  | [Assortment Name Translation (AssortmentTranslations sidecar)](features/assortment-name-translation.md) | Serves Assortment (product-grouping) **names** in multiple languages by adding a per-language **sidecar** table `AssortmentTranslations`, reusing the platform's | _underscore/Model/Client/AssortmentTranslation.php, dbchanges2/Client/2026-06-26a - AssortmentTranslations.sql, dbchanges2/Core/2026-06-26a - AssortmentTranslationsRecord.sql, dbchanges2/Client/2026-06-26b - AssortmentTranslationsAcl.sql |
8
8
  | [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 |
9
9
  | [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 |
10
+ | [Error Reporting — Issue/Event Aggregation (exceptionHandler)](features/error-reporting-issue-event.md) | `_underscore`'s global exception handler persists every uncaught exception into a two-table **Issue / Event** model in the **shared Core Logs DB** (`_underscore | _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 |
10
11
  | [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 |
11
12
  | [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/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js |
12
13
  | [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 |
@@ -0,0 +1,95 @@
1
+ ---
2
+ title: Error Reporting — Issue/Event Aggregation (exceptionHandler)
3
+ framework: "2.0"
4
+ repo: _underscore
5
+ project: _Underscore
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-07-06
10
+ owners: ["dfranks"]
11
+ files:
12
+ - _underscore/Error.php
13
+ - _underscore/Model/Core/Logs/Issue.php
14
+ - _underscore/Model/Core/Logs/Event.php
15
+ - dbchanges2/Logs/2026-07-06 - Issue and Event tables.sql
16
+ related:
17
+ - ../architecture.md
18
+ - ./per-client-database-connections.md
19
+ - ../../dbchanges2/architecture.md
20
+ ---
21
+
22
+ ## Summary
23
+
24
+ `_underscore`'s global exception handler persists every uncaught exception into a
25
+ two-table **Issue / Event** model in the **shared Core Logs DB** (`_underscore::DB_LOGS`,
26
+ models under `Model/Core/Logs/`) — **not** the per-client Logs DB. An **Issue** is the
27
+ deduplicated record for a distinct error (keyed by a hash of the backtrace) carrying an
28
+ aggregate `occurrences` counter; an **Event** is one row per individual occurrence with
29
+ its own stack trace and context. This is platform-wide error-reporting infrastructure,
30
+ not client-specific.
31
+
32
+ ## Key files / entry points
33
+
34
+ - **`_underscore/Error.php`** — `exceptionHandler()` (registered via
35
+ `set_exception_handler`). Computes `$issueHash = md5(serialize($backtrace))`, then, only
36
+ when the Core-Logs register is present (`isset(\_Database::$_registers[_underscore::DB_LOGS])`),
37
+ upserts an `_Model_Core_Logs_Issue` and inserts an `_Model_Core_Logs_Event`, committing
38
+ the `DB_LOGS` transaction.
39
+ - **`_underscore/Model/Core/Logs/Issue.php`** — `const DATABASE = _underscore::DB_LOGS;
40
+ const TABLE = 'Issues';`. Fields: `id, uuid, dtCreated, dtLastOccurred, urgency, subject,
41
+ description, clickupIdentifier, hash, errorMessage, occurrences, trace`.
42
+ - **`_underscore/Model/Core/Logs/Event.php`** — `const DATABASE = _underscore::DB_LOGS;
43
+ 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.
57
+
58
+ The `Issues`/`Events` tables live in the shared **Core Logs** DB, provisioned from
59
+ `dbchanges2/Logs/` (the `Logs/` folder targets the framework-level, non-tenant `Logs` DB
60
+ with unqualified table names — see the dbchanges2 architecture folder→DB mapping). Contrast
61
+ with per-client API/error logging in `Logs_<Tenant>` via `Model/Client/Logs/*`
62
+ (`DB_CLIENT_LOGS`, `dbchanges2/Logs_Client/`).
63
+
64
+ ## Data model
65
+
66
+ - **Issue** = one row per distinct error (dedup key = `hash`); `occurrences` is the rolling
67
+ count, `dtLastOccurred` the most-recent hit, `urgency` a list field, `clickupIdentifier`
68
+ links to an escalation ticket. There is **no `type` column** — it was dropped.
69
+ - **Event** = one row per occurrence, FK `issueId` → Issue, carrying the occurrence's own
70
+ `trace` and `context`.
71
+
72
+ ## Gotchas / known issues
73
+
74
+ - **`occurrences` must be incremented, not just seeded.** It was previously set to `0` only
75
+ at creation and never advanced, so the aggregate counter never moved. Fixed 2026-07-06 to
76
+ increment on every occurrence.
77
+ - **`Event.trace` must be set per occurrence.** The schema has a per-Event `trace` column
78
+ that was never populated; now recorded on every Event.
79
+ - **Persistence is conditional on the Core-Logs register.** If
80
+ `\_Database::$_registers[_underscore::DB_LOGS]` isn't set, the whole Issue/Event block is
81
+ skipped silently — no error row is written.
82
+ - **Shared-schema migration collision.** Because `Issues`/`Events` live in the *shared* Core
83
+ Logs DB, any two dbchanges2 `Logs/` migrations that both `CREATE TABLE Issues`/`Events`
84
+ will collide — the second fails with "table already exists". Two separate error-reporting
85
+ workstreams introduced these same tables (one adding the escalation cron + an
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`.
89
+
90
+ ## Change history
91
+
92
+ - 2026-07-06 — Moved Issue/Event tables and exceptionHandler persistence from the per-client
93
+ Logs DB to the shared Core Logs DB and dropped the `type` column; fixed `occurrences` to
94
+ increment each occurrence and set `Event.trace` per occurrence; removed dead commented-out
95
+ per-client-logs persistence block. (dfranks)
@@ -16,7 +16,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
16
16
 
17
17
  ## 2.0 framework
18
18
 
19
- - **_underscore** (_Underscore) _(framework core)_ — 21 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
19
+ - **_underscore** (_Underscore) _(framework core)_ — 22 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
20
20
  - **worker2** (Worker) — 25 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
21
21
  - **api2** (API) — 7 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
22
22
  - **dbchanges2** (Database Changes) _(framework core)_ — 3 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.267",
3
+ "version": "1.0.268",
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",