toga-ai 1.0.309 → 1.0.310

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.
@@ -7,7 +7,7 @@ client: shared
7
7
  type: feature
8
8
  status: active
9
9
  updated: 2026-07-09
10
- owners: ["jcardinal", "dfranks"]
10
+ owners: ["jcardinal", "dfranks", "mhammontree"]
11
11
  files:
12
12
  - crons/tickets.php
13
13
  - crons/tickets_prod.php
@@ -40,6 +40,19 @@ TOGa Desk ingests support email into tickets through a cron-driven IMAP poller
40
40
  3. **Parse & route (`emailToTicket()`):** extract from/to/cc/subject/body (with encoding fixes);
41
41
  match an existing ticket via `In-Reply-To` + `Message-ID` headers and **plus-addressing**
42
42
  (`support+<ticketnum>@…`). Matched ⇒ `addReply()`; otherwise ⇒ `add()`.
43
+ - **Noise suppression (before any ticket/attachment is created):** both feeders drop known
44
+ automated no-noise mail via the shared helper
45
+ `Ticket::isSuppressedNoiseEmail(?string $from, ?string $subject): bool` (static, in
46
+ `class.ticket.php`). It returns true only when the sender matches `/^no-?reply@/` **AND**
47
+ the subject starts with `staples po ` — both required so a real person is never blocked, and
48
+ the sender check is **domain-agnostic** (matches `noreply@togatech.com` /
49
+ `noreply@goagilant.com` and survives future sender-domain flips; the Staples PO alert sender
50
+ flipped `goagilant.com → togatech.com` on 2026-06-02). Wired into BOTH intake feeders:
51
+ `crons/tickets.php` adds it as one more `&&`-clause in the existing IMAP intake guard
52
+ (alongside the delivery-status / `MAILER-DAEMON` / Compass auto-reply filters);
53
+ `crons/pipe.php` uses it as an early guard-and-`exit(0)` right after the `$from`/`$subject`
54
+ parse. Each feeder logs a "suppressed" line. (TRUE-79975 — ~815 such alerts since Jun 2024,
55
+ ~704 of them landing in the dept-1 "Support Desk" catch-all and being closed by hand.)
43
56
  4. **Side effects:** auto-create a user when the sender domain maps to an existing client; assign
44
57
  to a department via the department email lookup; save attachments locally then push to **S3**
45
58
  (`App_Cloud::copyFileToS3()`, bucket `asifiles`); optionally generate Talos/TogaIQ summaries.
@@ -49,7 +62,12 @@ TOGa Desk ingests support email into tickets through a cron-driven IMAP poller
49
62
  ## Data model
50
63
  Writes `tickets`, `tickets_replies`, attachment `files`; reads config from the `config` table
51
64
  (`tickets_defaultdepartment`, `tickets_defaultclient`, `auto_close_tickets`,
52
- `email_from_address`, `timezone`). Department routing keys on `tickets_departments.email`.
65
+ `email_from_address`, `timezone`). Department routing keys on a `tickets_departments.email`
66
+ `LIKE` match against the To/CC addresses; **anything unmatched falls through to config
67
+ `tickets_defaultdepartment` (= dept 1 "Support Desk" catch-all) and `tickets_defaultclient`** —
68
+ which is why unrouted/automated mail piles into "Support Desk". There is **no allowlist/blocklist
69
+ config framework**: noise filtering is ad-hoc inline guards in the crons (the hardcoded Compass
70
+ "Automatic reply:" filter and now `isSuppressedNoiseEmail`).
53
71
 
54
72
  ## Gotchas / known issues
55
73
  - **`$processName` guard:** `tickets.php` throws if executed directly instead of via an env
@@ -59,7 +77,13 @@ Writes `tickets`, `tickets_replies`, attachment `files`; reads config from the `
59
77
  - **Loop prevention** only excludes FROM==department-TO addresses; CC/reply-all across multiple
60
78
  department addresses can still loop.
61
79
  - **Auto-reply filters** are partly hardcoded (e.g. ignore `TechHub@compass-usa.com`
62
- "Automatic reply:" messages).
80
+ "Automatic reply:" messages), and now the `isSuppressedNoiseEmail` Staples-PO filter — there
81
+ is no config-driven allow/block list, so each new noise source is another inline guard.
82
+ - **`Ticket::extractEmail()` returned an undefined offset on a no-match** (bare `$matches[0]`).
83
+ With a sender-less/malformed email this codebase promotes the warning to a thrown error
84
+ (`E_ALL` + `App_Error::handleError`), so an intake cron could **halt**. Hardened to
85
+ `return $matches[0] ?? '';` (TRUE-79975) — important because `isSuppressedNoiseEmail` routes
86
+ nullable/malformed sender input through `extractEmail`, putting this latent bug on the hot path.
63
87
  - Attachments are deleted locally immediately after the S3 upload with **no retry** if S3 fails.
64
88
  - **`adminid` clobber on reply routing:** after `emailToTicket()` sets `$data['adminid']` from
65
89
  the FROM address (~line 1407), an asset lookup (~line 1437-1442) overwrites it with the
@@ -69,6 +93,10 @@ Writes `tickets`, `tickets_replies`, attachment `files`; reads config from the `
69
93
  classification. See the ticket-lifecycle doc.
70
94
 
71
95
  ## Change history
96
+ - 2026-07-09 — added `Ticket::isSuppressedNoiseEmail()` noise suppression (no-reply sender + "Staples PO"
97
+ subject) wired into both feeders before ticket creation; hardened `extractEmail()` no-match to
98
+ `''`; documented the `tickets_defaultdepartment` → "Support Desk" catch-all fall-through
99
+ (TRUE-79975) (mhammontree)
72
100
  - 2026-07-09 — documented the `emailToTicket()` adminid-overwrite reply misclassification
73
101
  (TRUE-80114 planning investigation) (dfranks)
74
102
  - 2026-06-15 — documented from a source read of `crons/tickets*.php` and `emailToTicket()` (jcardinal)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.309",
3
+ "version": "1.0.310",
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",