toga-ai 1.0.118 → 1.0.119

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.
@@ -5,5 +5,6 @@
5
5
  | [_underscore Framework Architecture](architecture.md) | `_underscore` is the shared PHP backend framework for **all 2.0 applications**. | _underscore/_underscore.php, _underscore/Loader.php, _underscore/Framework.php, _underscore/Model.php, _underscore/Database.php, _underscore/Query.php, _underscore/Route.php, _underscore/Component.php |
6
6
  | [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/ItemFulfillments/TrackingNumber.php, _underscore/Component/Library/Carriers/Ups/Ups.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 |
7
7
  | [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 |
8
+ | [Per-Client Database Connections & the Local Logs Trap](features/per-client-database-connections.md) | When `_underscore` serves a request for a client it opens **three distinct per-client database connections**, not one. | _underscore/Database.php, _underscore/ApiRequest.php, _underscore/Model/Client/Logs/Api.php |
8
9
  | [Recursive Item Fulfillments (upstream mirroring)](features/recursive-item-fulfillments.md) | In a multi-tier supply chain a sales order (SO) spawns a purchase order (PO) that becomes another SO downstream, and so on. | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillmentItem.php, _underscore/Model/Client/ItemFulfillmentItemUnit.php, _underscore/Model/Client/ItemFulfillmentPackage.php, _underscore/Model/Compass/AdvanceShippingNotice.php, dbchanges2/Core/2026-02-13 - 75601 - RecursiveItemFulfillmentCreation.sql, dbchanges2/Core/2026-06-04 - RecursiveItemFulfillmentPut.sql |
9
10
  | [Tracking-Number Bridge Migration (ASN / Item Fulfillment / Item Receipt)](features/tracking-number-bridges.md) | Shipment tracking numbers used to live as **scalar FK columns** (`trackingNumberId`, `returnTrackingNumberId`) directly on the lowest-level "unit"/"item" tables | api2/Component/Api/V2/V2.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnit.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillmentItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Prudential/AdvanceShippingNotice.php, _underscore/Model/Compass/AdvanceShippingNotice.php, _underscore/Trait/Netsuite/ItemFulfillment.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql |
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: Per-Client Database Connections & the Local Logs Trap
3
+ framework: "2.0"
4
+ repo: _underscore
5
+ project: _Underscore
6
+ client: shared
7
+ type: feature
8
+ status: active
9
+ updated: 2026-06-18
10
+ owners: ["dfranks"]
11
+ files:
12
+ - _underscore/Database.php
13
+ - _underscore/ApiRequest.php
14
+ - _underscore/Model/Client/Logs/Api.php
15
+ related:
16
+ - ../architecture.md
17
+ ---
18
+
19
+ ## Summary
20
+
21
+ When `_underscore` serves a request for a client it opens **three distinct per-client
22
+ database connections**, not one. Each is an alias whose real schema name comes from Core's
23
+ `Databases` table for that client. Missing any one of them locally makes `mysqli_connect`
24
+ throw `Unknown database` and the whole request returns **HTTP 500 / EO-1** — even when the
25
+ actual work succeeded. The most common victim is the **logs** connection, because API
26
+ transaction logging is on by default.
27
+
28
+ ## Key files / entry points
29
+
30
+ - **`_underscore/Database.php`** — `connect()` (line ~410) resolves a connection *alias*
31
+ (`Client`, `ClientLogs`, `Archive`) to the concrete schema via the Core join that selects
32
+ `ClientDatabase.name`, `LogDatabase.name`, `ArchiveDatabase.name` (lines ~64–137). The alias
33
+ `_underscore::DB_CLIENT_LOGS` maps to the per-client `Logs_<Id>` schema.
34
+ - **`_underscore/ApiRequest.php`** (line ~240) — writes each API transaction to the logs DB
35
+ via `_Model_Client_Logs_Api` (falls back to `_Model_Core_Logs_Api` only when the client-logs
36
+ register points at the same DB as core-logs). This is what fires the logs connection.
37
+ - **`_underscore/Model/Client/Logs/Api.php`** — `const DATABASE = _underscore::DB_CLIENT_LOGS;
38
+ const TABLE = 'Api';` — the model behind the logged row.
39
+
40
+ ## How it works
41
+
42
+ The three per-client aliases and their concrete local schemas:
43
+
44
+ | Alias | Schema (per client) | Holds |
45
+ |---|---|---|
46
+ | `Client` (data) | `Client_<Id>` | operational data |
47
+ | `ClientLogs` (`DB_CLIENT_LOGS`) | `Logs_<Id>` | **API transaction logging** |
48
+ | `Archive` | `Archive_<Id>` | historical snapshots |
49
+
50
+ `<Id>` is the client's `clientIdentifier` from `Core.Clients` (e.g. `Growrk` → `Client_Growrk`,
51
+ `Logs_Growrk`, `Archive_Growrk`). A `Logs_<Id>` schema has 5 tables:
52
+ `Api, CustomRecordField, Error, Record, RecordField`.
53
+
54
+ ## Data model
55
+
56
+ `Logs_<Id>.Api` columns: `id, uuid, dtStamp, transactionId, apiId, userId, isAuthRequest,
57
+ direction, source, sourceIp, instanceId, executionTimeSeconds, method, hostname, route,
58
+ responseCode, queryString, requestHeaders, requestPayload, responseHeaders, responsePayload`.
59
+ Engine InnoDB, `utf8mb4_unicode_ci`.
60
+
61
+ ## Client variations
62
+
63
+ None — uniform across clients; only the `<Id>` suffix differs.
64
+
65
+ ## Gotchas / known issues
66
+
67
+ - **The "logs DB write trap" (local dev).** A laptop usually imports only `Client_<Id>`, not
68
+ `Logs_<Id>`/`Archive_<Id>`. Because logging is **on by default**, the first request 500s on
69
+ `Unknown database 'logs_<id>'` from `ApiRequest.php`'s logging write — *after* the real work
70
+ ran — so it reads like a mysterious outage, not a missing schema. The trace shows
71
+ `_Database::connect("ClientLogs","READ")` → `mysqli_connect(..., "Logs_<Id>")`.
72
+ - **Fix once and for all locally:** for every client you run locally, create all three schemas.
73
+ For logs, structure-only is enough (you never need prod log rows locally):
74
+ `mysqldump -h reader1.logs.database.togahub.com -u admin -p<pw> --no-data --no-tablespaces
75
+ --set-gtid-purged=OFF --column-statistics=0 Logs_<Id> | mysql -u root Logs_<Id>` (create the
76
+ empty DB first). Data DB comes from `reader1.client.database.togahub.com` (see the local-DB
77
+ refresh notes). This is a **local-environment** gap, not a code bug — do not "fix" it by
78
+ disabling logging in shared code.
79
+ - Related 1.0 analogue: the legacy `App_` worker has the same hazard writing to `Logs.API`
80
+ (`db_logs`) — the laptop trap there is documented separately in the worker NetSuite bootstrap
81
+ notes.
82
+
83
+ ## Change history
84
+
85
+ - 2026-06-18 — Documented after the `Logs_Growrk`/`Logs_Aig` local 500s while testing the
86
+ NetSuite TOGa Supply wrappers; created the missing local log schemas structure-only. (dfranks)
87
+
88
+ ## Related docs
89
+
90
+ - [_underscore architecture](../architecture.md)
91
+ - [NetSuite → TOGa Supply per-client sync](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md) — the work that surfaced this.
@@ -14,7 +14,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
14
14
 
15
15
  ## 2.0 framework
16
16
 
17
- - **_underscore** (_Underscore) _(framework core)_ — 7 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
17
+ - **_underscore** (_Underscore) _(framework core)_ — 8 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
18
18
  - **worker2** (Worker) — 8 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
19
19
  - **api2** (API) — 3 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
20
20
  - **dbchanges2** (Database Changes) _(framework core)_ — 1 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.118",
3
+ "version": "1.0.119",
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",