toga-ai 1.0.267 → 1.0.269
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.
- package/knowledge/1.0/apps/library/INDEX.md +1 -0
- package/knowledge/1.0/apps/library/features/cron-execution-monitoring.md +71 -0
- package/knowledge/2.0/apps/_underscore/INDEX.md +1 -0
- package/knowledge/2.0/apps/_underscore/features/error-reporting-issue-event.md +95 -0
- package/knowledge/INDEX.md +2 -2
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [Library (1.0 Framework) Architecture](architecture.md) | `library` is the shared library repository for **all 1.0 (legacy) applications** — the `App_` framework. | library/_.php, library/app/, library/browser/ |
|
|
6
6
|
| [App_Sso — Reusable 1.0 SSO Initiation (SP-initiated SAML via saml.togahub.com)](features/app-sso-initiation.md) | `App_Sso` (`library/app/sso.php`) is the **1.0 port of the 2.0 SAML gateway's SP-initiated SSO initiation**, packaged as a reusable, framework-level capability | library/app/sso.php, library/sso/togahub_private_key.key |
|
|
7
|
+
| [Cron Execution Monitoring (App_Framework check-in/out → CronJobExecutions)](features/cron-execution-monitoring.md) | `App_Framework::cronInitialization()` / `App_Framework::cronFinished()` (in `library/app/framework.php`) give every 1.0 (`App_`) cron job a check-in/check-out l | library/app/framework.php |
|
|
7
8
|
| [Diagnostic Dialog — View Recommended Services Routing](features/diagnostic-dialog-view-recommended-services.md) | `App_Model_Toga_Diagnostic::initializeDiagnosticDialog()` renders the device modal used across all TOGa service request views. | library/app/model/toga/diagnostic.php |
|
|
8
9
|
| [Elite Freshservice Sync (library)](features/elite-freshservice-sync.md) | `App_Api_Toga2` in `library/app/api/toga2.php` orchestrates bidirectional sync between TOGA 2 and TOGaDesk. | library/app/api/toga2.php |
|
|
9
10
|
| [Branded HTML Email Templates (App_Email_Template)](features/email-templates.md) | `App_Email_Template` (`app/email/template.php`) is the base class for branded HTML emails in the 1.0 (`App_`) framework. | library/app/email/template.php, library/app/email/agilant.php |
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Cron Execution Monitoring (App_Framework check-in/out → CronJobExecutions)
|
|
3
|
+
framework: "1.0"
|
|
4
|
+
repo: library
|
|
5
|
+
project: Library
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-07-06
|
|
10
|
+
owners: [dfranks]
|
|
11
|
+
files:
|
|
12
|
+
- library/app/framework.php
|
|
13
|
+
related:
|
|
14
|
+
- ../../worker/architecture.md
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Summary
|
|
18
|
+
|
|
19
|
+
`App_Framework::cronInitialization()` / `App_Framework::cronFinished()` (in
|
|
20
|
+
`library/app/framework.php`) give every 1.0 (`App_`) cron job a check-in/check-out
|
|
21
|
+
lifecycle recorded in the **`CronJobExecutions`** table in **`db_common`**. On start the job
|
|
22
|
+
INSERTs an execution row; on finish it UPDATEs that row with completion state and an
|
|
23
|
+
`executionTimeSeconds` duration. This is the shared-DB monitoring surface for 1.0 crons
|
|
24
|
+
across all 1.0 apps (worker, togadesk, toga, …), independent of the older `Log`/`CRON_START`
|
|
25
|
+
row in `db_log` and of the per-job Sentry check-in monitors.
|
|
26
|
+
|
|
27
|
+
## How it works
|
|
28
|
+
|
|
29
|
+
- **`cronInitialization()`** — INSERTs one row into `CronJobExecutions` (`db_common`) via the
|
|
30
|
+
execution-id registry variable, records start time, and returns/stores the execution id used
|
|
31
|
+
to close the row later.
|
|
32
|
+
- **`cronFinished()`** — UPDATEs the same `CronJobExecutions` row (matched by execution id)
|
|
33
|
+
with completion status and `executionTimeSeconds`.
|
|
34
|
+
- **Connection:** every `CronJobExecutions` read/write goes against **`db_common`**. The table
|
|
35
|
+
exists **only** in `db_common` — it does **not** exist on the `db_log` connection.
|
|
36
|
+
- **Duration precision:** `executionTimeSeconds` is `round(microtime(true) - $start, 3)` —
|
|
37
|
+
millisecond precision landing in a `decimal(10,3)` column. Do **not** `(int)`-cast it; the
|
|
38
|
+
cast silently discards the sub-second precision even when the schema column is `decimal(10,3)`.
|
|
39
|
+
- **Schema:** the table is provisioned by the dbchanges migration
|
|
40
|
+
`dbchanges/Common/DF/2026-5-7 Cron Checkin.sql` (table `CronJobExecutions`,
|
|
41
|
+
`executionTimeSeconds decimal(10,3)`, no `note` column).
|
|
42
|
+
- **Design decision:** 1.0 records executions by writing **directly to the shared DB**
|
|
43
|
+
(`db_common`), *not* by POSTing from 1.0 to a 2.0 API endpoint. Per Jeff Cardinal, 1.0 code
|
|
44
|
+
must not POST to 2.0 code; the direct shared-DB write is the sanctioned path (TRUE-78182).
|
|
45
|
+
|
|
46
|
+
## Gotchas
|
|
47
|
+
|
|
48
|
+
- **`db_log` has no `CronJobExecutions` table.** A `cronFinished()` UPDATE that runs against the
|
|
49
|
+
`db_log` connection throws on **every** 1.0 cron job. Both the INSERT and the UPDATE must
|
|
50
|
+
target `db_common`.
|
|
51
|
+
- **A `git merge` can silently resurrect pre-refactor logic.** These functions were cleanly
|
|
52
|
+
consolidated to a single `db_common`-only INSERT/UPDATE pair (commit `0a3a6f75`), but a later
|
|
53
|
+
`git merge origin/_production` (`509614036605…`) reintroduced the old dual-path structure from
|
|
54
|
+
`_production`'s still-unmigrated copy of the file — leaving duplicate INSERT/UPDATE blocks, and
|
|
55
|
+
reverting one path's connection argument back to `db_log`. Automatic merge resolution updated
|
|
56
|
+
the SQL text (renamed the table) but restored the wrong connection name, producing a
|
|
57
|
+
platform-wide breaking bug disguised as a simple PR-feedback rework. **When a merge touches a
|
|
58
|
+
function you already refactored on your branch and the base branch never got that refactor,
|
|
59
|
+
diff against the merge-base — do not just eyeball the current file** — to catch resurrected
|
|
60
|
+
logic. (Fixed 2026-07-06: consolidated back to one `db_common` INSERT/UPDATE pair.)
|
|
61
|
+
|
|
62
|
+
## Change history
|
|
63
|
+
- 2026-07-06 — Fixed a merge (`origin/_production`) that reintroduced duplicate
|
|
64
|
+
`CronJobExecutions` writes and pointed one `cronFinished()` UPDATE at `db_log` (where the
|
|
65
|
+
table doesn't exist), which would have thrown on every 1.0 cron. Consolidated to one
|
|
66
|
+
`db_common` INSERT/UPDATE pair and switched `executionTimeSeconds` to `round(…, 3)` for the
|
|
67
|
+
`decimal(10,3)` precision the migration provides. (dfranks, TRUE-78182)
|
|
68
|
+
- 2026-05-07 — Introduced `CronJobExecutions` check-in/out in `App_Framework` cron lifecycle;
|
|
69
|
+
direct `db_common` write chosen over a 1.0→2.0 API POST. (dfranks)
|
|
70
|
+
</content>
|
|
71
|
+
</invoke>
|
|
@@ -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)
|
package/knowledge/INDEX.md
CHANGED
|
@@ -4,7 +4,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
4
4
|
|
|
5
5
|
## 1.0 framework
|
|
6
6
|
|
|
7
|
-
- **library** (Library) _(framework core)_ —
|
|
7
|
+
- **library** (Library) _(framework core)_ — 11 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
|
|
8
8
|
- **worker** (Worker) — 12 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
|
|
9
9
|
- **togadesk** (TOGa Desk) — 8 doc(s) → [1.0/apps/togadesk/INDEX.md](1.0/apps/togadesk/INDEX.md)
|
|
10
10
|
- **togaview** (TOGa View) — 6 doc(s) → [1.0/apps/togaview/INDEX.md](1.0/apps/togaview/INDEX.md)
|
|
@@ -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)_ —
|
|
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