toga-ai 1.0.926 → 1.0.927

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: Library
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-21
9
+ updated: 2026-10-01
10
10
  owners: [jcardinal, mhammontree, bala]
11
11
  files:
12
12
  - library/app/api/toga2.php
@@ -23,6 +23,7 @@ related:
23
23
  - ../../worker/features/netsuite-togasupply-per-client-sync.md
24
24
  - ../architecture.md
25
25
  - ./error-capture-1-0.md
26
+ - ../../../clients/wje/features/oneuptime-ticket-writes-monitor.md
26
27
  - ../../../clients/elite/features/togadesk-service-request-intake.md
27
28
  ---
28
29
 
@@ -145,7 +146,9 @@ A large bi-directional sync gated by **two checkpoint parameters** stored in 2.0
145
146
 
146
147
  Signature takes `$integrationType` (`TICKETS` or `REPAIR_ORDERS`) plus **seven independent enable flags** and an optional `$monitorTogadeskDepartmentIds[]`:
147
148
  - **2.0 → TOGaDesk** (`$isEnabledToga2ToTogadeskIntegration`): pages 2.0 `/tickets` and `/ticket-notes` changed since the checkpoint; routes each to `syncToga2TicketIntoTogadesk1{RepairOrder|Ticket}` by `$integrationType`. Per-ticket exceptions are caught and sent to Sentry so one bad ticket doesn't abort the batch.
148
- - **TOGaDesk → 2.0** (`$isEnabledTogadeskToToga2Integration`): queries the 1.0 `db_togadesk` DB directly (SQL over `repair_orders`/`repair_order_history` or `tickets`/`tickets_replies`/`comments`, using `GREATEST(...)` of reply/comment timestamps) for the client's department(s), then calls `syncTogaDesk1{RepairOrder|Ticket}IntoToga2Ticket($id)`.
149
+ - **TOGaDesk → 2.0** (`$isEnabledTogadeskToToga2Integration`): queries the 1.0 `db_togadesk` DB directly (SQL over `repair_orders`/`repair_order_history` or `tickets`/`tickets_replies`/`comments`, using `GREATEST(...)` of reply/comment timestamps) for the client's department(s) — only the ones the cron passes (`$monitorTogadeskDepartmentIds`) — then calls `syncTogaDesk1{RepairOrder|Ticket}IntoToga2Ticket($id)`.
150
+ - **Link = `referenceId`.** `tickets.referenceId` = 2.0 ticket uuid; `tickets_replies.referenceId` / `comments.referenceId` = 2.0 ticket-note uuid. Written back **only after a successful POST**. Rows created by the 2.0 → 1.0 direction already carry it. So NULL/`''` `referenceId` = not (yet) in 2.0 — the basis of the [WJE ticket-writes monitor](../../../clients/wje/features/oneuptime-ticket-writes-monitor.md).
151
+ - A reply with a blank message falls back to `newstatus`; if still PHP-empty (NULL, `''`, `'0'`) it is skipped and never gets a `referenceId`. Comments are always sent.
149
152
  - Five further one-way 2.0 → TOGaDesk sub-syncs, each its own flag: Contacts→People, Items/Units→Assets, Predefined-Replies→TicketsPr, Ticket-Teams→Groups, Ticket-Categories→Category/Subcategory/Items.
150
153
  - Finishes by PUT-ing both checkpoint parameters back to 2.0.
151
154
 
@@ -167,6 +170,7 @@ Signature takes `$integrationType` (`TICKETS` or `REPAIR_ORDERS`) plus **seven i
167
170
  - **Two unrelated `sync_togasupply_<client>.php` families.** Files under `worker/crons/toga2/netsuite/` are the **NetSuite supply importer** (`require` the common engine). Files under `worker/crons/toga2/<client>/` (e.g. `aig/`, `wje/`) are this **bridge** (`syncWithToga`/`syncWithTogadesk`). Same filename, different integration, different parameter keys — they coexist harmlessly. Confirm which folder before editing.
168
171
  - **`syncWithToga` and the bridge build SQL by hand** with `App_Database::sqlEscape()` rather than the `App_Model` layer; follow the surrounding escaping discipline when modifying.
169
172
  - **Per-record error isolation** in the TOGaDesk sync is via `try/catch` → `\Sentry\captureException`; a thrown exception elsewhere (transport, checkpoint read) still aborts the whole run.
173
+ - **⚠ A failed ticket is never retried.** After a per-ticket exception the run still advances `TOGA_LAST_TICKET_INTEGRATION_DATETIME` past it, so the row stays unsent until someone touches the ticket again (new reply) or the checkpoint is moved back. A checkpoint-freshness check reads healthy through this — monitor NULL `referenceId` instead.
170
174
  - **Naming `fields` can SUPPRESS a nested relation that a plain request returns.** Observed 2026-08-12 on `GET /states`: a bare request comes back with the nested `country`, but adding a `fields` list drops it. This is the mirror image of the reverse-hasMany rule below — when a nested object you expected is missing, suspect the field list before the data.
171
175
  - **A reverse hasMany collection is only returned if you add it to the fetch `fields` whitelist.** The `/contacts` fetch requests an **explicit** `fields` list from the 2.0 metadata API; a reverse hasMany collection (e.g. `contactEmailAddresses.emailAddress`) is **not** returned unless added — so consuming a related collection means editing **both** the fetch field list **and** the consuming code. (The general 2.0-side metadata behavior — reverse relations resolved via the child model's `FOREIGNKEY_MODEL` and named as the camelCase plural of the child model — is a framework-level fact captured separately.)
172
176
  - **⚠ Depth alone does NOT return every reverse-hasMany collection — `GET /sales-orders` at depth 4 omits `itemFulfillmentItems` and `invoiceItems`.** Verified 2026-08-17 against prod `Logs_Compass.Api` responses (no fields whitelist): a depth-4 sales-order GET returns `salesOrderItems` and the **PO-link bridge** collections (`salesOrderItemPurchaseOrderItems`, `purchaseOrderItemSalesOrderItems`) but **not** `itemFulfillmentItems` or `invoiceItems`. So any importer code that guards on `$salesOrderItem->itemFulfillmentItems` / `->invoiceItems` from that GET is a **silent no-op** — same reverse-hasMany trap as above, with a concrete consequence: the NetSuite SO renumber fix keys off a recorded "could-not-delete" set instead of trusting those collections (see [per-client sync](../../worker/features/netsuite-togasupply-per-client-sync.md)).
@@ -180,6 +184,7 @@ Signature takes `$integrationType` (`TICKETS` or `REPAIR_ORDERS`) plus **seven i
180
184
  - **Pre-existing (low severity, out of scope): a raw-vs-escaped comparison causes a redundant UPDATE every sync.** At the update path (~line 8584) `syncContactToToga1Customer` compares `$existingCustomer['emailAddress'] !== $emailAddress` — the DB value from `App_Database::fetchRow()` is **unescaped**, while `$emailAddress` has already been through `App_Database::sqlEscape()`. For any value containing `'`, `"` or `\` the two can **never** compare equal, so that column is rewritten on **every** run. Same pattern applies to `businessName` and `phoneNumber` just above. **Predates TRUE-79401**, is a redundant write (not data loss), and was deliberately left out of that ticket's scope — recorded here so a future reader knows it is known.
181
185
 
182
186
  ## Change history
187
+ - 2026-10-01 — Documented the `referenceId` link/write-back, blank-reply skip rule, and that a per-ticket failure advances the checkpoint (never retried). (mhammontree)
183
188
  - 2026-09-21 — Recorded a pre-existing committed JWT (dev/NYCHH token) at `toga2.php:641` inside `send()` (the sync call path); flagged to rotate + delete, location-only. (jcardinal)
184
189
 
185
190
  ## Related
@@ -6,7 +6,7 @@ project: Worker
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-25
9
+ updated: 2026-10-01
10
10
  owners: ["jcardinal", "mhammontree", "bala", "rgirish"]
11
11
  files:
12
12
  - worker2/Worker/Monitor/Compass.php
@@ -18,6 +18,7 @@ files:
18
18
  - worker2/Worker/Monitor/Prudential.php
19
19
  - worker2/Worker/Monitor/Nycdoe.php
20
20
  - worker2/Worker/Monitor/Staples.php
21
+ - worker2/Worker/Monitor/Wje.php
21
22
  - worker2/Config/production.ini
22
23
  - worker2/Config/beta.ini
23
24
  - worker2/Config/sandbox-dev.ini
@@ -32,6 +33,7 @@ related:
32
33
  - ../../../clients/staples/features/oneuptime-sftp-folder-monitors.md
33
34
  - ../../../clients/compass-usa/features/oneuptime-ma-refresh-order-monitor.md
34
35
  - ../../../clients/compass-usa/features/oneuptime-netsuite-duplicate-sales-orders-monitor.md
36
+ - ../../../clients/wje/features/oneuptime-ticket-writes-monitor.md
35
37
  ---
36
38
 
37
39
  OneUptime push-metric monitor pattern for worker2 (2.0): a self-contained cron action decides pass/fail and POSTs a string token to a OneUptime Incoming Request monitor; open when building/tuning a worker2 monitor, its thresholds, or the OneUptime import JSON.
@@ -302,6 +304,13 @@ has no proven connection to the legacy `Common` DB today**, so a 2.0 monitor can
302
304
  `MaRefreshOrderExceptions` counts rows in `Client_Compass` instead. If worker2 ever gets that
303
305
  connection, a generic "1.0 cron has not checked in" monitor becomes possible.
304
306
 
307
+ This is about `Common` only — **worker2 does read other legacy DBs.** `worker2/_.php` registers
308
+ `TOGaDeskSupport` as `_underscore::DB_TOGADESK1` and `Vision_Log` as `DB_VISION_LOGS` at boot (from the
309
+ `[database1]` config group), already used in prod by `Worker/Sync/SalesOrderStatus.php` and
310
+ `Worker/Client/Aig/ClosedClaims.php`. A 2.0 monitor over TOGaDesk data is possible and preferred (it
311
+ runs outside the 1.0 worker it watches) — e.g. the
312
+ [WJE ticket-writes monitor](../../../clients/wje/features/oneuptime-ticket-writes-monitor.md).
313
+
305
314
  ### Match the lateness criterion to the flow's real time-scale
306
315
 
307
316
  The sibling Compass monitors ship 6 criteria including a DEGRADED incident at **2 missed pushes (10
@@ -559,4 +568,5 @@ production deploy.**
559
568
  - [OneUptime 1.0 worker uptime monitoring](../../../1.0/apps/worker/features/oneuptime-worker-uptime-monitoring.md) — the 1.0 push-heartbeat predecessor
560
569
 
561
570
  ## Change history
571
+ - 2026-10-01 — Clarified worker2 reads `TOGaDeskSupport` (`DB_TOGADESK1`) — only `Common` is unreachable; added `Monitor/Wje`. (mhammontree)
562
572
  - 2026-09-25 — Added `NetsuiteDuplicateSalesOrders`, the first monitor that queries NetSuite (SuiteQL) instead of a Toga DB. (rgirish)
@@ -6,8 +6,8 @@ project: Worker
6
6
  client: wje
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-09-16
10
- owners: ["snaredla"]
9
+ updated: 2026-10-01
10
+ owners: ["snaredla", "mhammontree"]
11
11
  files:
12
12
  - worker2/Worker/Wje.php
13
13
  - _underscore/Component/Api/Wje/Wje.php
@@ -24,9 +24,11 @@ files:
24
24
  - worker/crons/toga2/wje/sync_togasupply_wje.php
25
25
  - worker/crons/notifications/reports/wje/wje_common.php
26
26
  - library/app/systemmonitor/wje.php
27
+ - worker2/Worker/Monitor/Wje.php
27
28
  - dbchanges2/Client_Wje/2024-10-04 - WjeOnboarding.sql
28
29
  related:
29
30
  - clients/wje/profile.md
31
+ - clients/wje/features/oneuptime-ticket-writes-monitor.md
30
32
  - 2.0/apps/worker2/features/elite-freshservice-sync.md
31
33
  - 1.0/apps/library/features/toga2-api-client-and-bridge.md
32
34
  - 2.0/apps/worker2/features/startech-webhook-handler.md
@@ -62,7 +64,7 @@ a **daily batch import** of supporting records. A separate 2-minute cron bridges
62
64
  `crons/toga2/wje/sync_togasupply_wje.php` (2-min Togadesk bridge),
63
65
  `crons/notifications/reports/wje/{wje_monthly_report,wje_quarterly_report,wje_common}.php`.
64
66
  - **Models:** `_underscore/Model/Wje/*` (8 classes, see Data model).
65
- - **Monitoring:** `library/app/systemmonitor/wje.php` (`App_SystemMonitor_WJE`).
67
+ - **Monitoring:** `library/app/systemmonitor/wje.php` (`App_SystemMonitor_WJE`, Freshservice/webhook errors); `worker2/Worker/Monitor/Wje.php` ([TOGaDesk → 2.0 write monitor](../../../clients/wje/features/oneuptime-ticket-writes-monitor.md)).
66
68
 
67
69
  ### Real-time webhook (`_Worker_Wje::Webhook`)
68
70
 
@@ -115,7 +117,7 @@ asset type "Laptop", urgencies (Low/Normal/High/Critical), system params
115
117
  `TOGADESK_LAST_TICKET_INTEGRATION_DATETIME` / `TOGA_LAST_TICKET_INTEGRATION_DATETIME`.
116
118
 
117
119
  Hardcoded IDs: `CLIENT_UUID_WJE=c2f5180f-…`, `API_UUID_WJE=4750650f-…` (and `API_SECRET_WJE`) in
118
- `worker2/Worker/Wje.php`; TOGaDesk client `153`, dept `269` (internal `290`); default group
120
+ `worker2/Worker/Wje.php`; TOGaDesk client `153`, dept `269` (dept `290` is defined but not synced TOGaDesk → 2.0 — see `clients/wje/profile.md`); default group
119
121
  `13000155420` / agent `13000771757`; category form field `13000126389`.
120
122
 
121
123
  ## Gotchas
@@ -134,6 +136,9 @@ Hardcoded IDs: `CLIENT_UUID_WJE=c2f5180f-…`, `API_UUID_WJE=4750650f-…` (and
134
136
  - **W12** TicketStages use Freshservice status codes (Open=2 … Closed=5); status sync depends on those matching live Freshservice statuses.
135
137
  - Secrets (`API_SECRET_WJE`, the Freshservice password) are hardcoded in source — read them from the file when needed; never copy the values into docs or commits.
136
138
 
139
+ ## Change history
140
+ - 2026-10-01 — Corrected dept 290 (not synced 1.0 → 2.0); linked the new ticket-writes monitor. (mhammontree)
141
+
137
142
  ## Related
138
143
 
139
144
  - `clients/wje/profile.md` — WJE client profile + the 2-min Togadesk bridge sync.
@@ -20,7 +20,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
20
20
  ## 2.0 framework
21
21
 
22
22
  - **_underscore** (_Underscore) _(framework core)_ — 94 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
23
- - **worker2** (Worker) — 80 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
23
+ - **worker2** (Worker) — 81 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
24
24
  - **api2** (API) — 26 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
25
25
  - **dbchanges2** (Database Changes) _(framework core)_ — 22 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
26
26
  - **toga2-supply** (TOGa Supply) — 9 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
@@ -2,4 +2,5 @@
2
2
 
3
3
  | Doc | Framework | Summary |
4
4
  |-----|-----------|---------|
5
- | [Wiss, Janney, Elstner Associates, Inc.](profile.md) | 2.0 | WJE (Wiss, Janney, Elstner / "WJE IT") Freshservice help-desk client, single hardcoded tenant; open for the three sync paths and the TOGaDesk bridge ids/default |
5
+ | [WJE: TOGaDesk → TOGa 2 ticket-writes monitor (Monitor/Wje/TicketWritesToToga2)](features/oneuptime-ticket-writes-monitor.md) | 2.0 | worker2 OneUptime push monitor counting WJE TOGaDesk tickets/replies/comments never written to TOGa 2 (NULL referenceId); open when working on WJE bridge monito |
6
+ | [Wiss, Janney, Elstner Associates, Inc.](profile.md) | 2.0 | WJE (Wiss, Janney, Elstner / "WJE IT") Freshservice help-desk client, single hardcoded tenant; open for the three sync paths, the TOGaDesk bridge ids/department |
@@ -0,0 +1,80 @@
1
+ ---
2
+ title: "WJE: TOGaDesk → TOGa 2 ticket-writes monitor (Monitor/Wje/TicketWritesToToga2)"
3
+ framework: "2.0"
4
+ repo: worker2
5
+ project: Worker
6
+ client: wje
7
+ type: client-feature
8
+ status: active
9
+ updated: 2026-10-01
10
+ owners: ["mhammontree"]
11
+ files:
12
+ - worker2/Worker/Monitor/Wje.php
13
+ - dbchanges2/Core/2026-09-30b - Insert WJE Ticket Writes Monitor CronJob.sql
14
+ - worker/crons/toga2/wje/sync_togasupply_wje.php
15
+ - library/app/api/toga2.php
16
+ related:
17
+ - ../../../2.0/apps/worker2/features/oneuptime-worker2-monitoring.md
18
+ - ../../../1.0/apps/library/features/toga2-api-client-and-bridge.md
19
+ - ../../../2.0/apps/worker2/features/wje-freshservice-sync.md
20
+ - ../profile.md
21
+ ---
22
+ worker2 OneUptime push monitor counting WJE TOGaDesk tickets/replies/comments never written to TOGa 2 (NULL referenceId); open when working on WJE bridge monitoring, its departments, or clearing a stuck ticket.
23
+
24
+ ## Summary
25
+ `Monitor/Wje/TicketWritesToToga2` (`_Worker_Monitor_Wje`, TRUE-80523) watches the 1.0 → 2.0 half of the WJE TOGaDesk bridge. It follows the shared [OneUptime push-metric pattern](../../../2.0/apps/worker2/features/oneuptime-worker2-monitoring.md) — read that for payload contract, criteria, and provisioning.
26
+
27
+ - **Status 2026-10-01:** committed on branch `TRUE-80523` (worker2 + dbchanges2 + worker comment), **not deployed**. Cron seed ships `isActive = 0`; `$ONEUPTIME_URL` is still empty (push credential — never record it).
28
+ - Runs in worker2, outside the 1.0 worker it watches. Reads `TOGaDeskSupport` via `_underscore::DB_TOGADESK1` (see [worker2 legacy DB access](../../../2.0/apps/worker2/features/oneuptime-worker2-monitoring.md)).
29
+ - OneUptime monitor name: **"WJE Ticket Writes to TOGa 2"**.
30
+
31
+ ## How it works
32
+
33
+ ### Why a backlog, not a checkpoint-freshness check
34
+ The bridge catches a per-ticket exception → Sentry only, and **still advances `TOGA_LAST_TICKET_INTEGRATION_DATETIME` past it**. The failed ticket is never retried unless touched again. A checkpoint-age monitor would read healthy. A row with NULL `referenceId` catches both a dead cron and silent per-ticket failures (link model: [bridge doc](../../../1.0/apps/library/features/toga2-api-client-and-bridge.md)).
35
+
36
+ ### What it counts
37
+ WJE rows (`tickets.clientid = 153`) in `tickets`, `tickets_replies`, `comments` where `referenceId` is NULL or `''`, created on/after `BACKLOG_CUTOFF_DATE = '2025-05-01'`, in the configured departments, minus `excludedTicketNumbers`.
38
+
39
+ Skip rules mirror `syncTogaDesk1TicketIntoToga2Ticket` in `library/app/api/toga2.php` (~L12280-12290): a reply with a blank message falls back to `newstatus` and is skipped if still PHP-empty (NULL, `''`, `'0'`) — it never gets a `referenceId`, so the monitor skips it too. Comments are always sent.
40
+
41
+ ### Decision + payload
42
+ - `alarm: HIGH` when any counted row is `>= alertThresholdMinutes` old; else `alarm: OK`.
43
+ - `status: error` on DB failure (payload carries **only** `errorClass` + `errorNumber` — the exception message can hold SQL, DB host, DB user) and on empty `departmentIds` (`error: no_departments_configured`).
44
+ - Body: `latePendingCount`, `pendingCount`, per-type counts, `oldestPendingMinutes`, `alertThresholdMinutes`, `lateTicketNumbers` (top 10), `departmentIds`, `excludedTicketCount`.
45
+
46
+ ### Parameters (`Core.CronJobs.parameters`, no deploy to change)
47
+ `{"alertThresholdMinutes":15,"departmentIds":[269],"excludedTicketNumbers":[]}`
48
+ - `departmentIds` PHP default is `[]` **on purpose** — the DB row is the only home of the list. Missing list → `status: error`, never a silent hardcoded dept. The cron comment in `sync_togasupply_wje.php` points here; keep the two in step if the sync's departments change.
49
+ - `excludedTicketNumbers` — TOGaDesk ticket numbers the team gives up on, so one permanently failing ticket does not hold the alarm red.
50
+
51
+ ### Clear a stuck ticket (runbook)
52
+ 1. Fix the cause (Sentry has the exception).
53
+ 2. Add a reply to the ticket in TOGaDesk so the sync picks it up again — or move the checkpoint back.
54
+ 3. If abandoning it, add its ticket number to `excludedTicketNumbers`.
55
+
56
+ ### Activation (in order)
57
+ 1. Create the OneUptime Incoming Request monitor with **body rules only**: Contains `"alarm":"HIGH"` → Offline + incident; Contains `"status":"error"` → Offline + incident; Contains `"alarm":"OK"` → Online.
58
+ 2. Paste its URL into `$ONEUPTIME_URL`.
59
+ 3. Push and deploy worker2.
60
+ 4. Run the Core seed `2026-09-30b - Insert WJE Ticket Writes Monitor CronJob.sql`.
61
+ 5. Test HIGH: set `departmentIds` to `[290]` (has old unsent rows), hand-run. Restore `[269]`, hand-run to see recovery.
62
+ 6. Set `isActive = 1`.
63
+ 7. Only then add the not-received 10/15 min rules (see the activation-ordering trap in the shared doc).
64
+
65
+ ## Gotchas
66
+ - **Dept 269 is very low volume** (Sept 2026: 3 tickets, 7 replies, 2 comments). "No writes" is normal — this is why it is a backlog monitor, not a "writes stopped" monitor.
67
+ - **Blind spot: field-only edits.** Priority/assignee change on an already-linked ticket creates no row, so a failed push is invisible. Status changes are covered (they arrive as replies).
68
+ - **Clock is US/Central.** `TOGaDeskSupport` `NOW()` and row timestamps are both server US/Central (the `_underscore` driver sets no session `time_zone`). Spring DST can false-alarm once; autumn can delay an alarm up to 1h.
69
+ - **Pre-cutoff history never drains:** 21 sendable unsent dept-269 rows 2024-12 → 2025-04-22 (early integration). That is why the cutoff is 2025-05-01.
70
+ - **Query cost (prod, verified):** `tickets` via index_merge(departmentid, clientid); `tickets_replies` via `ticketid` index; `comments` has **no** `ticketid` index → full scan of ~13k rows (acceptable). Timestamp columns have no `ON UPDATE`.
71
+ - **Do not add 290 casually.** Dept 290 is not synced 1.0 → 2.0 today (see [profile](../profile.md)); pointing the monitor at it would alarm on the known backlog, not a new failure.
72
+ - **The ClickUp TRUE-80523 description is fabricated** (Talos auto-generated) — see [MCP tool usage gotcha](../../../standalone/apps/claude/workflows/mcp-tool-usage.md). Trust this doc and the code.
73
+
74
+ ## Related
75
+ - [OneUptime push-metric monitors for 2.0 workers](../../../2.0/apps/worker2/features/oneuptime-worker2-monitoring.md) — shared pattern
76
+ - [App_Api_Toga2 bridge](../../../1.0/apps/library/features/toga2-api-client-and-bridge.md) — `syncWithTogadesk`, `referenceId` write-back
77
+ - [WJE profile](../profile.md)
78
+
79
+ ## Change history
80
+ - 2026-10-01 — Created: monitor built on branch TRUE-80523, not deployed, cron `isActive = 0`. (mhammontree)
@@ -4,22 +4,26 @@ framework: "2.0"
4
4
  apps:
5
5
  - worker
6
6
  - library
7
+ - worker2
8
+ - dbchanges2
7
9
  project: Library
8
10
  client: wje
9
11
  type: profile
10
12
  status: active
11
- updated: 2026-09-16
12
- owners: [jcardinal]
13
+ updated: 2026-10-01
14
+ owners: [jcardinal, mhammontree]
13
15
  files:
14
16
  - worker/crons/toga2/wje/sync_togasupply_wje.php
15
17
  - worker2/Worker/Wje.php
16
18
  - worker/crons/toga2/wje/import_supporting_records.php
19
+ - worker2/Worker/Monitor/Wje.php
17
20
  related:
18
21
  - ../../1.0/apps/library/features/toga2-api-client-and-bridge.md
19
22
  - ../../2.0/apps/worker2/features/wje-freshservice-sync.md
23
+ - ./features/oneuptime-ticket-writes-monitor.md
20
24
  ---
21
25
 
22
- WJE (Wiss, Janney, Elstner / "WJE IT") Freshservice help-desk client, single hardcoded tenant; open for the three sync paths and the TOGaDesk bridge ids/defaults.
26
+ WJE (Wiss, Janney, Elstner / "WJE IT") Freshservice help-desk client, single hardcoded tenant; open for the three sync paths, the TOGaDesk bridge ids/departments, and its monitor.
23
27
 
24
28
  ## Summary
25
29
 
@@ -31,9 +35,19 @@ Data moves between WJE and TOGA on three paths:
31
35
 
32
36
  1. **Real-time Freshservice webhook** → `_Worker_Wje::Webhook` (worker2) — creates/updates tickets, contacts, assets, groups, categories, and conversation notes, keyed by `c_wje*` custom fields.
33
37
  2. **Daily batch import** (`worker/crons/toga2/wje/import_supporting_records.php`) — imports canned responses, requesters, assets, groups, and the 3-level category hierarchy.
34
- 3. **2-minute TOGaDesk bridge** (`App_Api_Toga2::syncWithTogadesk`) — the cron `worker/crons/toga2/wje/sync_togasupply_wje.php` enables the **full** set of bridge sub-syncs (tickets both directions, ticket-notes, contacts→people, items/units→assets, predefined-replies, ticket-teams→groups, ticket-categories) for TOGaDesk client id 153, department 269 (plus internal-tickets department 290).
38
+ 3. **2-minute TOGaDesk bridge** (`App_Api_Toga2::syncWithTogadesk`) — the cron `worker/crons/toga2/wje/sync_togasupply_wje.php` enables the **full** set of bridge sub-syncs (tickets both directions, ticket-notes, contacts→people, items/units→assets, predefined-replies, ticket-teams→groups, ticket-categories) for TOGaDesk client id 153. **TOGaDesk → 2.0 syncs department 269 only.**
39
+
40
+ Monitoring: [ticket-writes monitor](./features/oneuptime-ticket-writes-monitor.md) alarms when a dept-269 TOGaDesk row is not written to 2.0.
41
+
42
+ ## TOGaDesk departments
43
+
44
+ - **269** — tickets created in TOGaDesk (`153000xxx` numbers). Very low volume. Synced TOGaDesk → 2.0.
45
+ - **290** — WJE Freshservice tickets that arrive in TOGaDesk **from** 2.0 (ticket number = Freshservice INC number, e.g. `65239`; ~100-200/month, all carry `referenceId`). The cron defines `TOGADESK_WJE_INTERNAL_TICKETS_DEPARTMENT_ID = 290` but never passes it (`$monitorTogadeskDepartmentIds` not passed), so 290 is **not** synced TOGaDesk → 2.0. Confirmed with Jonathan 2026-09-30: only 269 is synced today.
46
+
47
+ **⚠ Known gap (not fixed):** a reply or comment typed in TOGaDesk on a dept-290 ticket is never written to 2.0 and never reaches WJE. Verified: ticket `65239`, a reply on 2026-08-18 has NULL `referenceId` and is absent from `Client_Wje.TicketNotes`. Since 2026-01 ~49 replies + 10 comments are stuck. **If 290 is ever added to the sync, its first run pushes that whole old backlog to WJE at once — decide what to do with it first.**
35
48
 
36
49
  ## Related
37
50
 
38
- - **[wje-freshservice-sync](../../2.0/apps/worker2/features/wje-freshservice-sync.md)** — full integration: webhook handler, import cron, `c_wje*` models, API clients, schema, reports, monitoring, gotchas.
51
+ - **[wje-freshservice-sync](../../2.0/apps/worker2/features/wje-freshservice-sync.md)** — full integration: webhook handler, import cron, `c_wje*` models, API clients, schema, reports, gotchas.
39
52
  - **[toga2-api-client-and-bridge](../../1.0/apps/library/features/toga2-api-client-and-bridge.md)** — the `syncWithTogadesk` bridge mechanism.
53
+ - **[oneuptime-ticket-writes-monitor](./features/oneuptime-ticket-writes-monitor.md)** — the 1.0 → 2.0 write monitor.
@@ -6,8 +6,8 @@ project: Claude Harness
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
9
- updated: 2026-09-25
10
- owners: ["jcardinal", "tcox", "ajean"]
9
+ updated: 2026-10-01
10
+ owners: ["jcardinal", "tcox", "ajean", "mhammontree"]
11
11
  files:
12
12
  - claude/.claude/skills/kickoff/SKILL.md
13
13
  - claude/.claude/skills/capture/SKILL.md
@@ -106,6 +106,7 @@ SELECT name, clientIdentifier FROM Clients WHERE name LIKE '%<keyword>%';
106
106
  - **⚠ "Beta" is `dev-sandbox`, NOT `client-beta`.** For the beta API tier (`api.beta.togahub.com`), `Core.DatabaseHosts` maps environment `beta` for `Client_*` schemas to the **dev-sandbox** cluster — so "the beta database" and "the dev-sandbox database" are the same host. The MCP's `client-beta` environment exposes **no schemas** and querying it looks like the data is missing. Query **`dev-sandbox`** whenever someone says "beta". Any frontend pointed at `VITE_API=https://api.beta.togahub.com/v2` is therefore reading/writing dev-sandbox data. (2024-era onboarding SQL shows beta on a us-west-2 `client-cluster`; that has since been consolidated — trust `Core.DatabaseHosts`, not old migrations.)
107
107
  - **⚠ The write-blocker is a naive string scan — it rejects `UPDATE`/`REPLACE` even inside a string literal.** `toga_query` refuses the whole statement if those words appear anywhere in it, so a read-only query like `… WHERE output LIKE '%skipped update%'` fails validation. **A column alias trips it too** — `… AS updateDisabled` is rejected on the same scan, so the alias has to be renamed (`skippedDisabled`), not just the predicate. Work around it by matching on a fragment that avoids the keyword (`LIKE '%disabled%'`, `LIKE '%skipped%'`), or by pulling the rows and filtering client-side — don't conclude the data isn't there.
108
108
  - **Production sessions render in US/Central (UTC−5); most other timestamps you're comparing are UTC.** GitHub/API/webhook timestamps are UTC, so a naive `dtStamp` vs. commit-time comparison is off by five hours — enough to make a healthy worker fleet look dead. Anchor both sides to UTC (or `CONVERT_TZ`) before drawing a conclusion about "when did this last run".
109
+ - **⚠ Talos auto-generated "KB investigation" text on a ClickUp ticket can be fabricated.** TRUE-80523's description called WJE "Write Job Entry" and cited a CloudWatch/Datadog ADR, a DLQ, and a Migration Sprint retro — none exist. Treat such write-ups as unverified; check every name and source against the code, this KB, and the data before acting on it.
109
110
  - **`local` is not this MCP.** If someone means their own machine's database, use the `mysql` CLI via shell, not `toga-db`.
110
111
  - **The client roster is wider than `registry.json`, and identifiers are not guessable.** `Core.Clients` held **46 clients** on 2026-07-30 against 17 in `registry.json`. A client absent from the registry is normal, not an error — the registry tracks clients whose *code knowledge* has been captured, not the full book of business. Identifiers do not follow from the display name, so **look them up, never construct them**:
111
112
 
@@ -123,6 +124,7 @@ SELECT name, clientIdentifier FROM Clients WHERE name LIKE '%<keyword>%';
123
124
  Schema names are **case-sensitive** — copy the exact value from `toga_list_schemas` or `Core.Clients`. `Client_Aig` works; `Client_AIG` and `client_aig` do not.
124
125
 
125
126
  ## Change history
127
+ - 2026-10-01 — Added gotcha: Talos auto-generated ClickUp ticket write-ups can invent sources (TRUE-80523). (mhammontree)
126
128
  - 2026-09-25 — corrected the "TOGa IQ" row: it now names the separate `toga25-iq` app, not the `talos` codebase (rebrand) (jcardinal)
127
129
 
128
130
  ## Related
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.926",
3
+ "version": "1.0.927",
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",