toga-ai 1.0.296 → 1.0.298
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/togadesk/INDEX.md +1 -1
- package/knowledge/1.0/apps/togadesk/features/email-to-ticket-intake.md +10 -2
- package/knowledge/1.0/apps/togadesk/features/ticket-lifecycle.md +48 -2
- package/knowledge/2.0/apps/_underscore/INDEX.md +1 -0
- package/knowledge/2.0/apps/_underscore/features/per-client-database-connections.md +4 -2
- package/knowledge/2.0/apps/_underscore/workflows/running-a-2.0-app-locally.md +124 -0
- package/knowledge/INDEX.md +1 -1
- package/package.json +1 -1
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
| [Ticket Email Notifications (notifications table)](features/notifications.md) | Which TOGa Desk emails fire for a given client is driven **entirely by data**, not code: the `TOGaDeskSupport.notifications` table holds one row per `(clientid, | desk/includes/classes/class.notification.php, crons/tickets.php, crons/tickets_prod.php |
|
|
9
9
|
| [REST API (RPC-over-POST) & API-Key Auth](features/rest-api.md) | TOGa Desk exposes a programmatic API at `desk/api/`. | desk/api/index.php, desk/api/resources/tickets.php, desk/api/resources/assets.php, desk/api/resources/authenticate.php, desk/includes/classes/class.apikey.php, desk/includes/functions.php |
|
|
10
10
|
| [SMB Contract Editing & the clientMspId Corruption Trap](features/smb-contract-editing.md) | The SMB contracts page (`/desk/?route=toga/smbcontracts&togaClientId=<id>`) edits `TOGA_*.SMBContracts` rows via a modal. | desk/template/modals/toga/smbcontracts/smbContract.php, desk/includes/controllers/modals/toga/smbcontracts/smbContract.php, desk/includes/controllers/actions/toga/smbcontracts/smbContract.php, desk/includes/controllers/actions/toga/smbcontracts/edit.php |
|
|
11
|
-
| [Ticket Lifecycle (class.ticket.php)](features/ticket-lifecycle.md) | All TOGa Desk ticket creation and reply handling funnels through `Ticket` in `desk/includes/classes/class.ticket.php`. | desk/includes/classes/class.ticket.php, desk/includes/controllers/actions.php, desk/api/resources/tickets.php, crons/tickets.php, desk/includes/controllers/actions/tickets/merge.php |
|
|
11
|
+
| [Ticket Lifecycle (class.ticket.php)](features/ticket-lifecycle.md) | All TOGa Desk ticket creation and reply handling funnels through `Ticket` in `desk/includes/classes/class.ticket.php`. | desk/includes/classes/class.ticket.php, desk/includes/controllers/actions.php, desk/includes/controllers/actions/tickets/addReply.php, desk/api/resources/tickets.php, desk/api/resources/ticket_replies.php, crons/tickets.php, crons/pipe.php, desk/includes/controllers/actions/tickets/merge.php |
|
|
12
12
|
| [Standalone PHP Test Script Bootstrap (TOGa Desk)](workflows/standalone-test-scripts.md) | How to write a standalone CLI PHP script that bootstraps the TOGa Desk framework for read-only testing of desk classes (e.g. | |
|
|
@@ -6,8 +6,8 @@ project: TOGa Desk
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["jcardinal"]
|
|
9
|
+
updated: 2026-07-09
|
|
10
|
+
owners: ["jcardinal", "dfranks"]
|
|
11
11
|
files:
|
|
12
12
|
- crons/tickets.php
|
|
13
13
|
- crons/tickets_prod.php
|
|
@@ -61,6 +61,14 @@ Writes `tickets`, `tickets_replies`, attachment `files`; reads config from the `
|
|
|
61
61
|
- **Auto-reply filters** are partly hardcoded (e.g. ignore `TechHub@compass-usa.com`
|
|
62
62
|
"Automatic reply:" messages).
|
|
63
63
|
- Attachments are deleted locally immediately after the S3 upload with **no retry** if S3 fails.
|
|
64
|
+
- **`adminid` clobber on reply routing:** after `emailToTicket()` sets `$data['adminid']` from
|
|
65
|
+
the FROM address (~line 1407), an asset lookup (~line 1437-1442) overwrites it with the
|
|
66
|
+
replying user's asset's assigned admin. On a matched-ticket reply this makes a user's email
|
|
67
|
+
look like a staff reply in `addReply()` (`isAdminReply=true`), so `Awaiting User` etc. never
|
|
68
|
+
transition. The asset-derived adminid is for NEW-ticket auto-assign in `add()`, not reply
|
|
69
|
+
classification. See the ticket-lifecycle doc.
|
|
64
70
|
|
|
65
71
|
## Change history
|
|
72
|
+
- 2026-07-09 — documented the `emailToTicket()` adminid-overwrite reply misclassification
|
|
73
|
+
(TRUE-80114 planning investigation) (dfranks)
|
|
66
74
|
- 2026-06-15 — documented from a source read of `crons/tickets*.php` and `emailToTicket()` (jcardinal)
|
|
@@ -6,13 +6,16 @@ project: TOGa Desk
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["mhammontree"]
|
|
9
|
+
updated: 2026-07-09
|
|
10
|
+
owners: ["mhammontree", "dfranks"]
|
|
11
11
|
files:
|
|
12
12
|
- desk/includes/classes/class.ticket.php
|
|
13
13
|
- desk/includes/controllers/actions.php
|
|
14
|
+
- desk/includes/controllers/actions/tickets/addReply.php
|
|
14
15
|
- desk/api/resources/tickets.php
|
|
16
|
+
- desk/api/resources/ticket_replies.php
|
|
15
17
|
- crons/tickets.php
|
|
18
|
+
- crons/pipe.php
|
|
16
19
|
- desk/includes/controllers/actions/tickets/merge.php
|
|
17
20
|
related:
|
|
18
21
|
- 1.0/apps/togadesk/features/smb-contract-editing.md
|
|
@@ -40,6 +43,26 @@ Creation paths — all funnel through `Ticket::add($data)`:
|
|
|
40
43
|
- Ticket merge (`actions/tickets/merge.php`) inserts a master ticket directly — it copies
|
|
41
44
|
fields from the child, including `customerid` (since June 2026).
|
|
42
45
|
|
|
46
|
+
### Reply entry points (FOUR doors — only two transition status)
|
|
47
|
+
There are **four** inbound reply paths, but the status-transition switch lives ONLY inside
|
|
48
|
+
`Ticket::addReply()`. Any door that does not route through `addReply()` will not change status:
|
|
49
|
+
- **IMAP-poll cron** — `crons/tickets.php` → `emailToTicket()` → `addReply()`. ✅ transitions.
|
|
50
|
+
- **Mail pipe** — `crons/pipe.php:113` → `emailToTicket()` (legacy 6-arg call) → `addReply()`.
|
|
51
|
+
✅ transitions.
|
|
52
|
+
- **Desk REST API** — `desk/api/resources/ticket_replies.php` `'add'` → `addReply($data)`
|
|
53
|
+
directly. ✅ transitions.
|
|
54
|
+
- **togaview customer portal** — the portal reply form (`action="/ticket"` + `newReplyBtn`,
|
|
55
|
+
from the generic `common/togaview/ticket.php` and client variants e.g.
|
|
56
|
+
`common/newcenturyholdingsllc/ticket.php`, `common/towfoundation/ticket.php`) POSTs to
|
|
57
|
+
`togaview/mvc/ticket/post.php` `newReplyBtn` handler (~line 388). This handler inserts the
|
|
58
|
+
reply row **directly** via `App_Model_TogaDesk_TicketReply` and **bypasses the `Ticket` class
|
|
59
|
+
entirely** — it never updates `tickets.status`, `tickets_replies.newStatus`, or
|
|
60
|
+
`tickets_history`. ❌ **no status transition.** This is the gap behind "a reply from outside
|
|
61
|
+
Desk doesn't move the ticket."
|
|
62
|
+
|
|
63
|
+
The in-Desk agent reply path is `desk/includes/controllers/actions/tickets/addReply.php` →
|
|
64
|
+
`Ticket::addReply($_POST)`.
|
|
65
|
+
|
|
43
66
|
## How it works
|
|
44
67
|
|
|
45
68
|
### `Ticket::deriveCustomerId(int, string): ?int` (added June 2026)
|
|
@@ -64,6 +87,11 @@ means the reply lands but the ticket status silently never changes (no history e
|
|
|
64
87
|
re-entry). That was the On Hold SLA bug; `case 'On Hold'` (admin ⇒ stays On Hold, client ⇒
|
|
65
88
|
Open) was added June 2026.
|
|
66
89
|
|
|
90
|
+
- **`origin` is the reliable "reply came from outside Desk" signal.** The in-app agent reply
|
|
91
|
+
path (`addReply.php` → `addReply($_POST)`) carries **no `origin` key**, whereas
|
|
92
|
+
`emailToTicket()` sets `tickets.origin = 'EMAIL'` and togaview sets `'TOGAVIEW'` on new
|
|
93
|
+
tickets. Absence of `origin` on a reply therefore identifies an in-app agent reply; presence
|
|
94
|
+
identifies an external reply. This is the safe hook for any "external reply → status" logic.
|
|
67
95
|
- Special case: an admin replying to their OWN ticket is treated as a user reply.
|
|
68
96
|
- Auto-assign on first staff reply requires the role perm `allowAutoAssign` via
|
|
69
97
|
profiles/profile_departments.
|
|
@@ -88,11 +116,29 @@ backfill (e.g. craftex:
|
|
|
88
116
|
column (close time would have to come from `tickets_replies.newStatus='Closed'` or
|
|
89
117
|
`tickets_history`). Implementing it is a product decision: what happens to a lapsed reply
|
|
90
118
|
(attach-but-closed / new ticket / bounce)?
|
|
119
|
+
- **`emailToTicket()` clobbers `adminid`, misclassifying user email replies as staff replies.**
|
|
120
|
+
After matching the sender and setting `$data['adminid']` from the FROM address (~line 1407),
|
|
121
|
+
an asset lookup (~line 1437-1442) **overwrites** `$data['adminid']` with the replying user's
|
|
122
|
+
asset's assigned admin. When the matched ticket goes to `addReply()`, the user's reply is
|
|
123
|
+
read as an admin/staff reply (`isAdminReply=true`), so states like `Awaiting User` stay put
|
|
124
|
+
instead of transitioning to `Open`. The asset-derived adminid is meant for auto-assigning
|
|
125
|
+
NEW tickets in `add()`, not for classifying replies — `adminid` serves two conflicting
|
|
126
|
+
purposes here.
|
|
127
|
+
- **Cross-app DB coupling:** togadesk and togaview share the `TOGaDeskSupport` database
|
|
128
|
+
(`tickets`, `tickets_replies`, `tickets_history`, `people`), so a reply/status behavior
|
|
129
|
+
change on either side is visible to both immediately. togaview cannot load Desk's
|
|
130
|
+
`class.ticket.php`, so its writes go through the `App_Model_TogaDesk_*` models
|
|
131
|
+
(`TicketReply`, `TicketsHistory`, `Ticket`) — any shared status logic must account for that
|
|
132
|
+
the portal path never instantiates `Ticket`.
|
|
91
133
|
- Legacy `togadesk/includes/class.ticket.php` has active email-reopen logic; the live
|
|
92
134
|
`desk/includes` version has it commented out — email replies never set status to "Reopened"
|
|
93
135
|
via `emailToTicket()`.
|
|
94
136
|
|
|
95
137
|
## Change history
|
|
138
|
+
- 2026-07-09 — mapped the four reply entry points (togaview portal bypasses `addReply()` so
|
|
139
|
+
never transitions status); documented `emailToTicket()` adminid-overwrite reply
|
|
140
|
+
misclassification, the `origin`-absence agent-reply signal, and togadesk↔togaview shared-DB
|
|
141
|
+
coupling — from TRUE-80114 planning source investigation (dfranks)
|
|
96
142
|
- 2026-06-12 — documented from craftex MSP portal debugging session (mhammontree)
|
|
97
143
|
- 2026-06 — added `deriveCustomerId()`; added `On Hold` case to addReply() switch; merge
|
|
98
144
|
copies `customerid` (mhammontree)
|
|
@@ -21,3 +21,4 @@
|
|
|
21
21
|
| [Surface Resolver (_Model_Core_Surface::resolve — replaces Page::meta)](features/surface-resolver.md) | The runtime for the platform-wide **Surface** UI presentation layer: 9 `_underscore` models plus a cached resolver, `_Model_Core_Surface::resolve(&$api, string | _underscore/Model/Core/Surface.php, _underscore/Model/Client/AclRecordScript.php, _underscore/Model/Core/RecordScript.php, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Client_Quad/2026-07-01a - GrantSurfacesMetaGroupScriptAcl.sql, dbchanges2/Client_CompassCanada/2026-07-01a - GrantSurfacesMetaGroupScriptAcl.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, _underscore/Model/Core/SurfaceElement.php, _underscore/Model/Core/Action.php, _underscore/Model/Core/Vocabulary.php, _underscore/Model/Core/VocabularyTerm.php, _underscore/Model/Core/Message.php, _underscore/Model/Client/SurfaceOverride.php, _underscore/Model/Client/MessageTranslation.php, _underscore/Model/Client/ThemeToken.php, _underscore/Model/Core/Page.php |
|
|
22
22
|
| [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, dbchanges2/Client_Prudential/2026-06-15 - ItemFulfillmentTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Quad/2026-06-18a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19b - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql, dbchanges2/Client_Quad/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql |
|
|
23
23
|
| [Units for Items for Purchase Orders — Data Structure](features/units-for-items-for-purchase-orders.md) | Describes how unit (serialized inventory) data is linked to sales-order and purchase-order line items behind the `units-for-items-for-purchase-orders` TableView | |
|
|
24
|
+
| [Running a 2.0 App Locally (browser, end-to-end via api2)](workflows/running-a-2.0-app-locally.md) | The full dependency chain required to run a 2.0 client app **through the browser**, end-to-end, against a **local `api2`** (e.g. | _underscore/Environment.php, _underscore/Config.php, _underscore/Database.php, _underscore/Route.php, api2/Component/Api/V2/V2.php, api2/index.php, api2/.htaccess |
|
|
@@ -6,8 +6,8 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["dfranks", "jcardinal"]
|
|
9
|
+
updated: 2026-07-09
|
|
10
|
+
owners: ["dfranks", "jcardinal", "mhammontree"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Database.php
|
|
13
13
|
- _underscore/ApiRequest.php
|
|
@@ -93,6 +93,7 @@ registration is region-aware in `api2/Controller/Index.php`; the alias const is
|
|
|
93
93
|
|
|
94
94
|
## Change history
|
|
95
95
|
|
|
96
|
+
- 2026-07-09 — Linked the new [Running a 2.0 app locally](../workflows/running-a-2.0-app-locally.md) runbook, which frames these three connections as one requirement of a full local browser run. (mhammontree)
|
|
96
97
|
- 2026-07-07 — Noted the new shared **Cache** cluster (`Databases` id 145, alias `DB_CACHE`,
|
|
97
98
|
region-aware) added for the api2 cross-client retrieval engine. (jcardinal)
|
|
98
99
|
- 2026-06-18 — Documented after the `Logs_Growrk`/`Logs_Aig` local 500s while testing the
|
|
@@ -101,4 +102,5 @@ registration is region-aware in `api2/Controller/Index.php`; the alias const is
|
|
|
101
102
|
## Related docs
|
|
102
103
|
|
|
103
104
|
- [_underscore architecture](../architecture.md)
|
|
105
|
+
- [Running a 2.0 app locally (browser end-to-end)](../workflows/running-a-2.0-app-locally.md) — the full local-stack runbook these three connections are one requirement of.
|
|
104
106
|
- [NetSuite → TOGa Supply per-client sync](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md) — the work that surfaced this.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Running a 2.0 App Locally (browser, end-to-end via api2)
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: _underscore
|
|
5
|
+
project: _Underscore
|
|
6
|
+
client: shared
|
|
7
|
+
type: workflow
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-07-09
|
|
10
|
+
owners: ["mhammontree"]
|
|
11
|
+
files:
|
|
12
|
+
- _underscore/Environment.php
|
|
13
|
+
- _underscore/Config.php
|
|
14
|
+
- _underscore/Database.php
|
|
15
|
+
- _underscore/Route.php
|
|
16
|
+
- api2/Component/Api/V2/V2.php
|
|
17
|
+
- api2/index.php
|
|
18
|
+
- api2/.htaccess
|
|
19
|
+
related:
|
|
20
|
+
- ../features/per-client-database-connections.md
|
|
21
|
+
- ../../dbchanges2/workflows/client-onboarding.md
|
|
22
|
+
- ../../dbchanges2/architecture.md
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Summary
|
|
26
|
+
|
|
27
|
+
The full dependency chain required to run a 2.0 client app **through the browser**,
|
|
28
|
+
end-to-end, against a **local `api2`** (e.g. pointing the `toga2-view` frontend at a laptop
|
|
29
|
+
`api2` instead of beta/prod). This is separate from CLI/script backend testing — it exercises
|
|
30
|
+
the web SAPI, the Apache-supplied environment, Core-driven client-DB registration, and the
|
|
31
|
+
`/v2/auth/login` browser auth gate, none of which the CLI path touches. It cost hours to
|
|
32
|
+
reverse-engineer; the notes below are platform-general (any 2.0 client app), with Rate used
|
|
33
|
+
only as the running example.
|
|
34
|
+
|
|
35
|
+
**Bottom line:** most local failures here are a **provisioning gap in the local stack**
|
|
36
|
+
(missing/stale DB, unregistered origin, unseeded Core), *not* an app bug. The confusing
|
|
37
|
+
`Route.php:525` "failed to determine how to render view" and stale-Core "missing model file"
|
|
38
|
+
fatals below both mean "the local stack isn't fully stood up."
|
|
39
|
+
|
|
40
|
+
> Cost/benefit note from the originating session: because standing the full local backend up
|
|
41
|
+
> is this involved, the team's chosen shortcut for front-end work was to **deploy api2 to beta
|
|
42
|
+
> and point the local frontend at beta** rather than rebuild the entire local backend stack.
|
|
43
|
+
|
|
44
|
+
## Steps / requirements
|
|
45
|
+
|
|
46
|
+
### 1. ENVIRONMENT comes from Apache for the web app (not the CLI)
|
|
47
|
+
|
|
48
|
+
- `_Environment::initialize()` reads `getenv('ENVIRONMENT')` (`_underscore/Environment.php:11`)
|
|
49
|
+
and **throws if unset**. `_Config` then loads `Config/<ENVIRONMENT>.ini` (`Config.php:38`).
|
|
50
|
+
- The **web app** gets `ENVIRONMENT` from **Apache** — a `SetEnv ENVIRONMENT <name>` in the
|
|
51
|
+
api2 vhost — *not* from your shell/CLI environment. Dev convention:
|
|
52
|
+
`ENVIRONMENT=dev-<name>-laptop` → loads `Config/dev-<name>-laptop.ini`.
|
|
53
|
+
- To see what the **web** app actually resolves (the frontend URL/console won't show it — api2
|
|
54
|
+
is a separate host), temporarily add to the top of `api2/index.php`:
|
|
55
|
+
`if (isset($_GET['__env'])) die(getenv('ENVIRONMENT'));` and hit `http://api2/?__env=1`.
|
|
56
|
+
Remove it afterward.
|
|
57
|
+
|
|
58
|
+
### 2. api2 collapses the environment name to a slug
|
|
59
|
+
|
|
60
|
+
- `V2.php:146`: `$this->environment = (substr($name,0,4)=='dev-') ? 'dev' : $name`. So **any**
|
|
61
|
+
`dev-*` INI resolves to the slug **`dev`** for downstream lookups (notably Core DB host
|
|
62
|
+
selection, below).
|
|
63
|
+
|
|
64
|
+
### 3. Client DB connections are Core-driven, not from the INI
|
|
65
|
+
|
|
66
|
+
- `_Database::registerClientDatabases($environment, $clientId)` (`_underscore/Database.php:56`)
|
|
67
|
+
SELECTs from **Core**, joining `Clients → Databases → DatabaseHosts → Environments(slug=<env>)`
|
|
68
|
+
to resolve the client's **client, log, AND archive** databases (all three INNER-JOINed — a
|
|
69
|
+
missing row for any one drops the whole registration).
|
|
70
|
+
- For local, Core must have an `Environments.slug='dev'` row plus `DatabaseHosts` rows pointing
|
|
71
|
+
all three DBs at `localhost`. A prod Core backup **already carries a `dev`→localhost row by
|
|
72
|
+
design** — do not assume it's missing.
|
|
73
|
+
- There are **two** log connections: `DB_LOGS` (base framework `Logs`) and `DB_CLIENT_LOGS`
|
|
74
|
+
(per-tenant `Logs_<tenant>`). See
|
|
75
|
+
[per-client database connections](../features/per-client-database-connections.md) for the
|
|
76
|
+
three-alias mechanism and the logs-DB write trap.
|
|
77
|
+
|
|
78
|
+
### 4. Browser login `POST /v2/auth/login` — requirements in the order code checks them
|
|
79
|
+
|
|
80
|
+
Checked in `V2.php` (~lines 485–800):
|
|
81
|
+
|
|
82
|
+
1. **POST with an `Origin` header.** A browser address-bar **GET** has neither → silent
|
|
83
|
+
fallthrough (no auth, no useful error). Requests must come from the frontend (fetch/XHR),
|
|
84
|
+
not a typed URL.
|
|
85
|
+
2. **`Core.Domains` row for the port-stripped origin.** `V2.php:98-103` strips the port, so
|
|
86
|
+
`http://rate.togaview:5173` matches a `Core.Domains` row for `http://rate.togaview`. That
|
|
87
|
+
row's `clientId`/`appId` must resolve to the local client/app.
|
|
88
|
+
3. **Active login user** must exist in `Client_<tenant>.Users`.
|
|
89
|
+
4. **All four databases must exist WITH SCHEMAS:** `Client_<tenant>`, `Logs_<tenant>`,
|
|
90
|
+
`Archive_<tenant>`, and base `Logs`. An **empty** database is not enough — every request
|
|
91
|
+
writes a transaction log via `$log->save()` at `execute()` finalization (`V2.php:2138`) into
|
|
92
|
+
`Logs_<tenant>.Api` / `Logs.Api`. Structure-only imports of the log/archive DBs are fine.
|
|
93
|
+
5. **`Core.Apis` must be seeded** — API auth uses `Apis.uuid`/`Apis.secret`. `Apis` is
|
|
94
|
+
deliberately **excluded from blanks/backups**, so a freshly restored Core often has it empty.
|
|
95
|
+
|
|
96
|
+
## Gotchas / known issues
|
|
97
|
+
|
|
98
|
+
- **The silent `Route.php:525` error** — `"Failed to determine how to render view for route
|
|
99
|
+
'/v2/...'"` (thrown at `_underscore/Route.php:525`) means the API handler returned **no**
|
|
100
|
+
response, so `_Route` fell through to view rendering. It is thrown during bootstrap
|
|
101
|
+
**outside** api2's JSON error handler, so the browser shows raw HTML and often **nothing** is
|
|
102
|
+
written to the PHP error log. The real cause is almost always a **local DB/registration gap**
|
|
103
|
+
(missing DB or schema, unresolved client-DB registration, or the auth gate being skipped per
|
|
104
|
+
step 4.1) — treat it as "the local stack isn't fully provisioned," not a request/app defect.
|
|
105
|
+
|
|
106
|
+
- **A STALE local Core silently 500s every api2 request.** api2's V2 loops **every**
|
|
107
|
+
`Core.Records` model at request time (`$thisModelName::TABLE`, `V2.php:3442-3446`). If local
|
|
108
|
+
Core is missing recent migrations it still references removed models and **fatals on a missing
|
|
109
|
+
model file** — a confusing error that looks unrelated to the DB and breaks *all* requests.
|
|
110
|
+
Concrete example: `dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql` is **non-idempotent**
|
|
111
|
+
(fixed record ids 317–322, RecordFields 2171–2200 — cannot be re-run) and its Section 9
|
|
112
|
+
**DELETES** Core `Records` row 41 (`ItemFulfillmentPackages`, model
|
|
113
|
+
`\_Model_Client_ItemFulfillmentPackage`), consolidating it into
|
|
114
|
+
`ItemFulfillments_TrackingNumbers` (record 317). A local Core missing this migration still has
|
|
115
|
+
record 41, so V2 fatals on the removed `_Model_Client_ItemFulfillmentPackage` file.
|
|
116
|
+
**Fix:** bring local Core current — apply the missing Core migrations in date order, or refresh
|
|
117
|
+
Core wholesale. Do not re-run a single non-idempotent migration in isolation.
|
|
118
|
+
|
|
119
|
+
## Change history
|
|
120
|
+
|
|
121
|
+
- 2026-07-09 — Documented the full local browser-run dependency chain (Apache `ENVIRONMENT`,
|
|
122
|
+
`dev-*`→`dev` slug collapse, Core-driven client-DB registration, `/v2/auth/login` browser gate,
|
|
123
|
+
the `Route.php:525` silent-fallthrough gotcha, and the stale-Core "missing model file" fatal),
|
|
124
|
+
discovered while trying to run `toga2-view` against a local `api2`. (mhammontree)
|
package/knowledge/INDEX.md
CHANGED
|
@@ -17,7 +17,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
17
17
|
|
|
18
18
|
## 2.0 framework
|
|
19
19
|
|
|
20
|
-
- **_underscore** (_Underscore) _(framework core)_ —
|
|
20
|
+
- **_underscore** (_Underscore) _(framework core)_ — 27 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
21
21
|
- **worker2** (Worker) — 27 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
22
22
|
- **api2** (API) — 10 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
23
23
|
- **dbchanges2** (Database Changes) _(framework core)_ — 3 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
package/package.json
CHANGED