toga-ai 1.0.490 → 1.0.491

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: Tools
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-07-30
9
+ updated: 2026-08-01
10
10
  owners: ["jcardinal"]
11
11
  files:
12
12
  - tools/mvc/errors/get.php
@@ -32,10 +32,10 @@ happening and were deliberately never ticketed. Nobody had that view before.
32
32
  ## How it works
33
33
 
34
34
  - **Curate** subject, description, `minimumUrgency`, assignee, and mute window.
35
- - **Manage per-client email recipients** (`IssueEmailAddresses`). `clientId = 0` means **all
35
+ - **Manage per-client email recipients** (`IssueEmailAddress`). `clientId = 0` means **all
36
36
  clients** and requires an explicit confirmation flag; the list view renders it as
37
37
  **"ALL CLIENTS"**.
38
- - **Merge fingerprints** — repoint an `IssueFingerprints` row at an existing Issue. Without this,
38
+ - **Merge fingerprints** — repoint an `IssueFingerprint` row at an existing Issue. Without this,
39
39
  the fingerprint/Issue split is inert: a refactor that shifts line numbers produces a new Issue
40
40
  and the curated one is orphaned.
41
41
  - **Any save sets `isManaged`**, which does double duty: it protects the curated text from being
@@ -59,8 +59,21 @@ from `Core.Databases` id **11** (`worker2` `CORE_LOGS_DATABASE_ID`); `Logs` is o
59
59
  `_underscore` *register alias*. Read the real name from `Core.Databases` before configuring a new
60
60
  environment, or the console silently points at a database that may not exist.
61
61
 
62
+ ## Table names are SINGULAR
63
+
64
+ The Core Logs tables this console queries are `Issue`, `Event`, `IssueFingerprint`,
65
+ `IssueClickupTask`, `IssueEmailAddress`, `IssueAreaOwner` — **singular**, unlike every other
66
+ table this app touches. That is the deliberate `Logs`-cluster convention (see the
67
+ [2.0 error-reporting doc](../../../2.0/apps/_underscore/features/error-reporting-issue-event.md)).
68
+ They were renamed from plural on 2026-08-01, after the pipeline was already in production.
69
+
62
70
  ## Gotchas / known issues
63
71
 
72
+ - **A 2.0 table rename breaks this page silently until it is run.** This console has **no model
73
+ layer** — `get.php` and `post.php` interpolate table names into hand-written `App_Database`
74
+ SQL (40+ literal occurrences between them). Nothing here fails at deploy time; it fails when
75
+ a curator opens the page. Any rename in the shared Core Logs DB must be applied to both files
76
+ in the same release as the migration.
64
77
  - **This is a 1.0 app reading a 2.0 shared database.** Access is plain `App_Database` via the
65
78
  `db_toga2logs` alias, not the `_Model` layer — so none of the 2.0 model protections apply.
66
79
  Escape every interpolated value and allowlist every identifier (see the 1.0 back-end standard).
@@ -72,6 +85,11 @@ environment, or the console silently points at a database that may not exist.
72
85
 
73
86
  ## Change history
74
87
 
88
+ - 2026-08-01 — Updated every hardcoded table name in `tools/mvc/errors/get.php` (25+ refs) and
89
+ `post.php` (15+ refs) for the Core Logs singular rename (`Issues`→`Issue`, `Events`→`Event`,
90
+ `IssueFingerprints`→`IssueFingerprint`, `IssueClickupTasks`→`IssueClickupTask`,
91
+ `IssueEmailAddresses`→`IssueEmailAddress`, `IssueAreaOwners`→`IssueAreaOwner`). Recorded that
92
+ this 1.0 console has no model layer to absorb a 2.0 shared-schema rename. (jcardinal)
75
93
  - 2026-07-30 — Built as part of TRUE-78188: new `/errors` triage/curation console over the
76
94
  shared Core Logs DB via a new `db_toga2logs` alias; curation sets `isManaged` (protects text
77
95
  and exempts from GC); fingerprint merging; per-client recipient management with an explicit
@@ -16,7 +16,7 @@
16
16
  | [Re-pointing a DB alias mid-request (_Database::register park/restore)](features/database-alias-repointing.md) | `_Database` keys **all live per-database runtime state by the connection ALIAS** (`Client` / `_underscore::DB_CLIENT`, `ClientLogs`, `Archive`), **not** by the | _underscore/Database.php, _underscore/Query.php, api2/Component/Api/V2/V2.php, api2/Component/Api/CrossClient/CrossClient.php |
17
17
  | [2.0 Email Send Pipeline (queue + Send worker)](features/email-send-pipeline.md) | In 2.0, `_Email::send()` **does not transmit** — it queues the message. | _underscore/Email.php, worker2/Worker/Infrastructure/Email/Send.php |
18
18
  | [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 |
19
- | [Error Reporting — Issue/Event Capture, Fingerprinting & Aggregation](features/error-reporting-issue-event.md) | Platform-wide error reporting for TOGA 2.0, built on an **Issue / Event** aggregation model in the **shared Core Logs DB**. | _underscore/Error.php, _underscore/Exception/Business.php, _underscore/Model/Core/Logs/Issue.php, _underscore/Model/Core/Logs/Event.php, _underscore/Model/Core/Logs/IssueFingerprint.php, _underscore/Model/Core/Logs/IssueClickupTask.php, _underscore/Model/Core/Logs/IssueEmailAddress.php, _underscore/Model/Core/Logs/IssueAreaOwner.php, dbchanges2/Logs/2026-07-30a - Error reporting Issues and Events.sql, dbchanges2/Core/2026-07-30a - Error escalation cron job.sql |
19
+ | [Error Reporting — Issue/Event Capture, Fingerprinting & Aggregation](features/error-reporting-issue-event.md) | Platform-wide error reporting for TOGA 2.0, built on an **Issue / Event** aggregation model in the **shared Core Logs DB**. | _underscore/Error.php, _underscore/Exception/Business.php, _underscore/Model/Core/Logs/Issue.php, _underscore/Model/Core/Logs/Event.php, _underscore/Model/Core/Logs/IssueFingerprint.php, _underscore/Model/Core/Logs/IssueClickupTask.php, _underscore/Model/Core/Logs/IssueEmailAddress.php, _underscore/Model/Core/Logs/IssueAreaOwner.php, dbchanges2/Logs/2026-07-30a - Error reporting Issues and Events.sql, dbchanges2/Logs/2026-08-01a - Rename tables to singular.sql, dbchanges2/Logs_Client/2026-08-01a - Drop Error table.sql, dbchanges2/Core/2026-07-30a - Error escalation cron job.sql |
20
20
  | [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 |
21
21
  | [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). | worker2/Component/Forecast/SaleImport/SaleImport.php, worker2/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 |
22
22
  | [isFulfillable Propagation Up the SO↔PO Chain](features/fulfillable-item-propagation.md) | `Items.isFulfillable` is a boolean that gates whether a storefront line's **Qty Fulfilled** cell is actionable. | _underscore/Model/Client/Item.php, _underscore/Model/Compass/Item.php, dbchanges2/Core/2026-07-17 - Items isFulfillable RecordField.sql, dbchanges2/Core/2026-07-17 - RegisterItemIsFulfillableInterceptors.sql, dbchanges2/Client/2026-07-17 - ItemsisFulfillable.sql |
@@ -6,7 +6,7 @@ project: _Underscore
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-07-30
9
+ updated: 2026-08-01
10
10
  owners: ["dfranks", "jcardinal"]
11
11
  files:
12
12
  - _underscore/Error.php
@@ -18,6 +18,8 @@ files:
18
18
  - _underscore/Model/Core/Logs/IssueEmailAddress.php
19
19
  - _underscore/Model/Core/Logs/IssueAreaOwner.php
20
20
  - dbchanges2/Logs/2026-07-30a - Error reporting Issues and Events.sql
21
+ - dbchanges2/Logs/2026-08-01a - Rename tables to singular.sql
22
+ - dbchanges2/Logs_Client/2026-08-01a - Drop Error table.sql
21
23
  - dbchanges2/Core/2026-07-30a - Error escalation cron job.sql
22
24
  related:
23
25
  - ../architecture.md
@@ -45,9 +47,23 @@ re-implementing persistence.
45
47
 
46
48
  ## Data model (shared Core Logs DB)
47
49
 
48
- Six tables, created by `dbchanges2/Logs/2026-07-30a - Error reporting Issues and Events.sql`:
49
- `Issues`, `IssueFingerprints`, `Events`, `IssueEmailAddresses`, `IssueClickupTasks`,
50
- `IssueAreaOwners`.
50
+ Six tables, created by `dbchanges2/Logs/2026-07-30a - Error reporting Issues and Events.sql` and
51
+ renamed to their final **singular** names by
52
+ `dbchanges2/Logs/2026-08-01a - Rename tables to singular.sql`:
53
+ `Issue`, `IssueFingerprint`, `Event`, `IssueEmailAddress`, `IssueClickupTask`, `IssueAreaOwner`.
54
+
55
+ > **⚠ The `Logs` cluster is the one place that uses SINGULAR table names.** Everywhere else on
56
+ > the platform tables are plural (`Orders`, `Customers`) — see the 2.0 back-end standard. In
57
+ > `Logs` the tables are named for the *thing* (`Issue`, `Event`), matching how they are referred
58
+ > to verbally ("the Issue table", "API Logs", not "APIs Logs"). **When you add a new table to the
59
+ > `Logs` cluster, name it singular.** Do not "fix" these to plural, and do not carry the singular
60
+ > form outside `Logs`.
61
+
62
+ Renamed 2026-08-01 (`Issues`→`Issue`, `Events`→`Event`, `IssueFingerprints`→`IssueFingerprint`,
63
+ `IssueClickupTasks`→`IssueClickupTask`, `IssueEmailAddresses`→`IssueEmailAddress`,
64
+ `IssueAreaOwners`→`IssueAreaOwner`). The migration renames the tables *and* their foreign keys
65
+ and indexes; the `const TABLE` on every `_Model_Core_Logs_*` model was updated to match. Older
66
+ notes below and in related docs may still quote the plural names — the shape is unchanged.
51
67
 
52
68
  **An Issue is never client-scoped.** An Issue is identified by *code*, and code is global — the
53
69
  same bug hitting six clients is **one** Issue. Per-client scoping lives on the occurrence
@@ -183,6 +199,18 @@ Sentry-removal ticket cannot complete until the 1.0 port does.
183
199
  `varchar(255)` (`errorMessage`, `subject`) **throws** on `->save()`. Inside the handler's
184
200
  `catch(Throwable)` the throw is swallowed and the row is silently dropped — precisely when a
185
201
  long message matters most. Truncate to column width before `save()`.
202
+ - **`const TABLE` must track a table rename, and nothing warns you.** A `_Model` subclass whose
203
+ `const TABLE` points at a renamed table fails only at query time. When a `Logs` table is
204
+ renamed, the matching `_underscore/Model/Core/Logs/*.php` `const TABLE` must change in the
205
+ **same** deploy as the migration.
206
+ - **1.0 apps break on a 2.0 table rename.** Tools' `/errors` console reads the shared Core Logs
207
+ DB with hand-written `App_Database` SQL, so it has no model layer to absorb a rename — every
208
+ literal table name in `tools/mvc/errors/get.php` and `post.php` had to be edited by hand.
209
+ Grep the 1.0 side before renaming anything in a **shared** 2.0 database.
210
+ - **The per-client `Logs_Client.Error` table is dead.** Superseded by the Issue/Event pipeline;
211
+ the code that wrote it is commented out in `_Error.php`, the `Model/Core/Logs/Error.php` and
212
+ `Model/Client/Logs/Error.php` models were deleted, and the table is dropped by
213
+ `dbchanges2/Logs_Client/2026-08-01a - Drop Error table.sql`. Do not reintroduce it.
186
214
  - **Shared-schema migration collision.** Because these tables live in the *shared* Core Logs
187
215
  DB, only **one** migration may `CREATE` them; later migrations only `ALTER`.
188
216
  - **Pre-existing committed secrets in this area (not fixed — out of scope, report only).**
@@ -239,6 +267,16 @@ clientUserId). **Neither was built.** As built instead:
239
267
 
240
268
  ## Change history
241
269
 
270
+ - 2026-08-01 — **Decided: `Logs` cluster tables use SINGULAR names.** Renamed all six
271
+ post-deployment (`Issues`→`Issue`, `Events`→`Event`, `IssueFingerprints`→`IssueFingerprint`,
272
+ `IssueClickupTasks`→`IssueClickupTask`, `IssueEmailAddresses`→`IssueEmailAddress`,
273
+ `IssueAreaOwners`→`IssueAreaOwner`) via
274
+ `dbchanges2/Logs/2026-08-01a - Rename tables to singular.sql` (tables + FKs + indexes), and
275
+ updated `const TABLE` on every `_Model_Core_Logs_*` model. This is a naming-convention
276
+ alignment, not a behavior change: the `Logs` cluster is named for the thing ("API Logs", not
277
+ "APIs Logs") while the rest of the platform stays plural. Also deleted the obsolete
278
+ `Model/Core/Logs/Error.php` and `Model/Client/Logs/Error.php` and dropped the per-client
279
+ `Logs_Client.Error` table. (jcardinal)
242
280
  - 2026-07-30 — Reworked TRUE-78188: six-table schema in the shared Core Logs DB (Issue is
243
281
  global by code; client scope lives on Events/IssueEmailAddresses); `_Error::captureException()`
244
282
  as the single capture entry point; trace-frame fingerprinting with the message excluded
@@ -6,7 +6,7 @@ project: Worker
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-07-30
9
+ updated: 2026-08-01
10
10
  owners: ["jcardinal"]
11
11
  files:
12
12
  - worker2/Worker/Infrastructure/Errors.php
@@ -71,7 +71,7 @@ range contains today.
71
71
 
72
72
  ### Recurrence (episodes)
73
73
 
74
- One `IssueClickupTasks` row per ClickUp **episode**. Closing the task fires a webhook that stamps
74
+ One `IssueClickupTask` row per ClickUp **episode**. Closing the task fires a webhook that stamps
75
75
  `dtResolved` and increments `recurrenceCount`. A later occurrence opens a **new** task linked to
76
76
  its predecessors via `POST /task/{id}/link/{links_to}` — a **link, not a dependency**; a
77
77
  dependency would *block* the task. A cooldown prevents recreating a task 60 seconds after
@@ -85,7 +85,7 @@ which is safe only because the automation never sets status itself.
85
85
 
86
86
  ### Area-level owner learning (observation only)
87
87
 
88
- `IssueAreaOwners` learns an owner after **5 in a row** and ships **observation-only** — a hint in
88
+ `IssueAreaOwner` learns an owner after **5 in a row** and ships **observation-only** — a hint in
89
89
  the task body, never an auto-assignment. Misassignment is expensive: a few wrong tickets and
90
90
  developers stop trusting the queue. Learning is per **area**, not per issue, because one issue
91
91
  rarely recurs enough to learn from before it is fixed.
@@ -119,7 +119,7 @@ recipients away** after a quiet month.
119
119
 
120
120
  - **The working-set query must be a UNION, not an OR.** It is a UNION of three separately
121
121
  indexable branches. MySQL will **not** `index_merge` across an `OR` when one branch is a
122
- correlated `EXISTS`, so the OR form **full-scans `Issues` every 60 seconds, forever**.
122
+ correlated `EXISTS`, so the OR form **full-scans `Issue` every 60 seconds, forever**.
123
123
  - **`INSERT ... SELECT ... WHERE NOT EXISTS` is not a concurrency guard** under REPEATABLE READ.
124
124
  The open-episode guard is a UNIQUE key on a VIRTUAL generated column, and the code **claims
125
125
  the episode before calling ClickUp** so a losing run fails before creating a duplicate
@@ -131,7 +131,7 @@ recipients away** after a quiet month.
131
131
  - **Business email bodies/subjects must not carry `errorMessage` or `trace`.** Both are seeded
132
132
  once from whichever client created the Issue and are never re-scoped. An uncurated business
133
133
  issue gets a **neutral** subject rather than another client's raw error text.
134
- - **`IssueEmailAddresses.clientId = 0` means "all clients"** and requires an explicit
134
+ - **`IssueEmailAddress.clientId = 0` means "all clients"** and requires an explicit
135
135
  confirmation flag in the Tools console; list views render it as **"ALL CLIENTS"**.
136
136
  - **The ClickUp webhook has no HMAC signature verification** (pre-existing, unfixed). A forged
137
137
  POST can mark issues acknowledged — freezing the neglect axis — or resolve episodes. That is
@@ -143,6 +143,12 @@ recipients away** after a quiet month.
143
143
 
144
144
  ## Change history
145
145
 
146
+ - 2026-08-01 — Table names updated for the Core Logs **singular** rename (`Issue`, `Event`,
147
+ `IssueFingerprint`, `IssueClickupTask`, `IssueEmailAddress`, `IssueAreaOwner`). Naming-only;
148
+ the cron's behavior is unchanged. See
149
+ [error-reporting-issue-event](../../_underscore/features/error-reporting-issue-event.md).
150
+ (jcardinal)
151
+
146
152
  - 2026-07-30 — Built as part of TRUE-78188: action renamed `SyncWithClickup` → `Escalate`; new
147
153
  `Worker/Clickup/ErrorTask.php`. Two-axis urgency (volume 20/50/100 + neglect 1/4/24) with a
148
154
  `minimumUrgency` floor; fast-up/slow-down with release bands 12/35/70 over 3 windows; a
@@ -58,7 +58,7 @@ Lambda, the EB worker tier + SQS delivery (see [worker2 architecture](../archite
58
58
 
59
59
  worker2 has **two** monitoring patterns and neither writes to the central **`Logs` Issues/Events**
60
60
  tables. That is deliberate — **`Logs.Issues`/`Logs.Events` are strictly for application
61
- errors/exceptions** that escalate to ClickUp or email (business-routed by `IssueEmailAddresses`
61
+ errors/exceptions** that escalate to ClickUp or email (business-routed by `IssueEmailAddress`
62
62
  presence; see the escalation-cron work). **Periodic health-check, integration-health, and
63
63
  data-quality RESULTS do NOT go there** — they belong in the Monitor framework:
64
64
 
@@ -5,7 +5,7 @@ project: _Underscore
5
5
  client: shared
6
6
  type: standard
7
7
  status: active
8
- updated: 2026-07-27
8
+ updated: 2026-08-01
9
9
  owners: [jcardinal, mhammontree]
10
10
  files: []
11
11
  related:
@@ -292,6 +292,34 @@ WHERE
292
292
 
293
293
  ### **SQL Naming Conventions**
294
294
 
295
+ #### **Table Names — plural, except the `Logs` cluster**
296
+
297
+ * Table names are **plural**: `Orders`, `Customers`, `PurchaseOrderItems`. This is the default
298
+ and applies to every database on the platform with one exception.
299
+ * **Exception — the shared Core `Logs` database uses SINGULAR table names**: `Issue`, `Event`,
300
+ `IssueFingerprint`, `IssueClickupTask`, `IssueEmailAddress`, `IssueAreaOwner`. The `Logs`
301
+ cluster is named for the *thing being logged*, matching how it is referred to verbally —
302
+ "API Logs", not "APIs Logs"; "the Issue table", not "the Issues table".
303
+ * **When adding a table to `Logs`, name it singular.** Do not "fix" existing `Logs` tables to
304
+ plural, and do not carry the singular form into any other database.
305
+ * Foreign keys are unaffected: a key referencing `Issue` is still `issueId` (the FK rule already
306
+ singularizes).
307
+
308
+ ```
309
+ # Good — non-Logs databases
310
+ Orders
311
+ Customers
312
+ Locations_LocationAttributes
313
+
314
+ # Good — the Logs database only
315
+ Issue
316
+ IssueFingerprint
317
+
318
+ # Bad
319
+ Logs.Issues # Logs tables are singular
320
+ Core.Order # non-Logs tables are plural
321
+ ```
322
+
295
323
  #### **Bridge Tables**
296
324
 
297
325
  * Most bridge tables need to include an underscore between the two tables they are joining. If you are joining a parent with a child table, the parent should come first. Exceptions may be made for inherent child records like `SalesOrderItems` being a child yet also a bridge of `SalesOrders`
@@ -756,3 +784,7 @@ client sending a shallow depth.
756
784
  ### Code Documentation
757
785
 
758
786
  * Ensure code is well-documented with comments and usage examples.
787
+
788
+ ## Change history
789
+
790
+ - 2026-08-01 — Recorded the Logs-cluster singular table-naming exception to the otherwise-plural table convention. (jcardinal)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.490",
3
+ "version": "1.0.491",
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",