toga-ai 1.0.347 → 1.0.348
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,8 +6,8 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["mhammontree"]
|
|
9
|
+
updated: 2026-07-15
|
|
10
|
+
owners: ["mhammontree", "ajean"]
|
|
11
11
|
files:
|
|
12
12
|
- Component/Api/V2/V2.php
|
|
13
13
|
related:
|
|
@@ -53,6 +53,18 @@ with ticket fields at the top level.
|
|
|
53
53
|
A minimal create needs only `shortDescription`/`longDescription` (+ optional `impact`, `ticketNotes`);
|
|
54
54
|
the FK references are all nullable.
|
|
55
55
|
|
|
56
|
+
## Reuse in TOGa Desk 2.0
|
|
57
|
+
|
|
58
|
+
The **TOGa Desk 2.0** build (the 2.0 rebuild of the 1.0 `togadesk` product) **reuses and
|
|
59
|
+
enhances this existing `tickets` Record (`_Model_Client_Ticket`) rather than rebuilding it** —
|
|
60
|
+
including its child arrays (`ticketNotes[]`, `ticketFiles[]`, `ticketUnits[]`, `ticketItems[]`,
|
|
61
|
+
`ticketUsers[]`, `ticketServiceQuestionAnswers[]`). The ticket stays the **atomic unit of work**.
|
|
62
|
+
The three higher-order Desk entities — **Service Request**, **Case**, and **Scope** — are
|
|
63
|
+
**net-new Records** modeled on this same nested child-array pattern (natural-identifier MATCH
|
|
64
|
+
references + inline-created children). Entity hierarchy is **Case → Service Request → Ticket**.
|
|
65
|
+
Before building new SLA behavior, verify/extend whatever this API already supports rather than
|
|
66
|
+
rebuilding it.
|
|
67
|
+
|
|
56
68
|
## GET / filtering
|
|
57
69
|
|
|
58
70
|
`GET /v2/tickets` supports `fields`, `where`, `join`/`ojoin`, `sort`, `group`, pagination
|
|
@@ -69,4 +81,5 @@ ticket's own contact: `join=Contacts@Contacts:Contacts.id=Tickets.contactId&wher
|
|
|
69
81
|
|
|
70
82
|
## Change history
|
|
71
83
|
|
|
84
|
+
- 2026-07-15 — Noted TOGa Desk 2.0 reuses/enhances this `tickets` Record (not a rebuild); Service Request / Case / Scope are net-new Records on the same child-array pattern (Case → SR → Ticket). Planning-only decision. (ajean)
|
|
72
85
|
- 2026-06-23 — Initial capture from the Fordham ticket integration + customer docs (TRUE-79699). (mhammontree)
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-15
|
|
10
10
|
owners: ["ajean"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Clickup/Design.php
|
|
@@ -24,20 +24,32 @@ replacing a set of native ClickUp automations. When a task's **Final Design Outc
|
|
|
24
24
|
drop-down is set to an actionable value, the worker either spawns an iteration task or
|
|
25
25
|
completes the task, and a daily cron reminds the Design Approver of pending reviews.
|
|
26
26
|
|
|
27
|
-
**Current state (as of 2026-07-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
**Current state (as of 2026-07-15):** the silent no-op fix (worker2 PR #98, commit `7a36564`)
|
|
28
|
+
and the extended iterated-automation review workflow are shipped and live. On top of that, an
|
|
29
|
+
intermittent "setting FDO to *Iterated – New Task* does NOT spawn" failure was fixed end-to-end
|
|
30
|
+
on **worker2 PR #113 + _underscore PR #669** (deploy together — see Data model). Two root causes
|
|
31
|
+
were fixed: (1) the spawn gate ignored design tasks living in a **Backlog Design list** (only the
|
|
32
|
+
True Design Sprints folder / hardcoded list IDs were admitted), and (2) `handleFinalDesignOutcome`
|
|
33
|
+
resolved the FDO from a stale per-process `getTaskDetails` snapshot taken before ClickUp reflected
|
|
34
|
+
the drop-down write (read-after-write race) — now the webhook's authoritative `after` value is
|
|
35
|
+
threaded in. Also hardened the shared `_Component_Api_Clickup::send()` reliability path and added
|
|
36
|
+
observability breadcrumbs. 55 test assertions pass; `php -l` clean. The native ClickUp automations
|
|
37
|
+
that were masking the original no-op must stay disabled — see Gotchas.
|
|
36
38
|
|
|
37
39
|
## Key files / entry points
|
|
38
40
|
- `Worker/Clickup/Design.php` → `_Worker_Clickup_Design` — the design-sprint engine.
|
|
39
|
-
- `handleFinalDesignOutcome(
|
|
40
|
-
on **all five** FDO options explicitly (no silent
|
|
41
|
+
- `handleFinalDesignOutcome(string $taskId, ?string $webhookFdoValue = null)` — the entry that
|
|
42
|
+
decides spawn vs. complete; branches on **all five** FDO options explicitly (no silent
|
|
43
|
+
fall-through — see How it works). The optional `$webhookFdoValue` is the authoritative new value
|
|
44
|
+
from the webhook, preferred over the (possibly stale) snapshot — see Gotchas (read-after-write).
|
|
45
|
+
- `isDesignSprintTask(...)` — the spawn/handler gate. Admits: the True Design Sprints folder,
|
|
46
|
+
**OR** (the Backlog folder `BACKLOG_FOLDER_ID` AND the list name matches `DESIGN_LIST_NAME_PATTERN`),
|
|
47
|
+
**OR** the `DESIGN_LIST_IDS` allowlist (fallback). Fixes the "backlog Design list silently skipped"
|
|
48
|
+
root cause — see Gotchas.
|
|
49
|
+
- `resolveFinalDesignOutcomeOptionId(object $taskDetails, ?string $overrideValue = null)` — maps the
|
|
50
|
+
drop-down value to an option ID; **string-compares** value/orderindex/id (see Gotchas). When
|
|
51
|
+
`$overrideValue` is given (the webhook `after` value) it wins over the snapshot's field value; the
|
|
52
|
+
option *list* is still read from the snapshot.
|
|
41
53
|
- `handleTaskEnteredIterated()` — **new**; fired when a task ENTERS "iterated" status. Sets
|
|
42
54
|
FDO = "In Review" then increments the Iteration custom field by +1 (idempotent — see
|
|
43
55
|
Gotchas). Folder-gated via `isInDesignSprintsFolder()`.
|
|
@@ -50,8 +62,17 @@ automations that were masking the no-op must stay disabled — see Gotchas.
|
|
|
50
62
|
and now all user comments; the duplicate no longer bumps Iteration (moved to the
|
|
51
63
|
iterated-entry trigger) and is set to FDO = "In Progress" post-create.
|
|
52
64
|
- `completeTask(...)` — closes an approved / cancelled task (also used on iterated → complete).
|
|
53
|
-
- `SendReviewReminders(...)` — daily reminder cron action
|
|
54
|
-
|
|
65
|
+
- `SendReviewReminders(...)` — daily reminder cron action; now also fetches the Backlog folder's
|
|
66
|
+
lists and includes those matching `DESIGN_LIST_NAME_PATTERN`, mirroring `isDesignSprintTask()` so
|
|
67
|
+
newly-created backlog Design lists get reminders without a code change.
|
|
68
|
+
- `findExistingSpawnedIteration(...)` — its previously-silent `catch` now logs.
|
|
69
|
+
- `TRUE_DESIGN_SPRINTS_FOLDER_ID`, `BACKLOG_FOLDER_ID` (`'90116192732'`), and
|
|
70
|
+
`DESIGN_LIST_NAME_PATTERN` (`/(?<!non-)\bdesign\b/i`) constants.
|
|
71
|
+
- `Worker/Clickup.php` `_Worker_Clickup::extractCustomFieldAfterValue(object $historyItem): ?string`
|
|
72
|
+
(**new**) — normalizes a webhook `history_items[].after` custom-field value (scalar orderindex /
|
|
73
|
+
object / JSON array with `id`|`orderindex`). Distinguishes a **missing** `after` (returns `null` →
|
|
74
|
+
caller falls back to the snapshot) from an **explicit null** `after` (returns `''` sentinel → field
|
|
75
|
+
was cleared → resolve to no outcome, do NOT act on the stale previous option).
|
|
55
76
|
- `Worker/Clickup.php` `Webhook` — the `switch ($historyItem->after->status)` block (see
|
|
56
77
|
Gotchas re: after vs. before) now has a `case _Worker_Clickup_Design::STATUS_ITERATED`
|
|
57
78
|
dispatching to `handleTaskEnteredIterated()`.
|
|
@@ -102,6 +123,14 @@ present (worker2 PR #80, _underscore PR #624).
|
|
|
102
123
|
throws undefined-constant. Both changes are on branch
|
|
103
124
|
`feature/design-sprint-iterated-automation`.
|
|
104
125
|
|
|
126
|
+
**Backlog Design lists (2026-07-15):** design tasks also live under the **Backlog folder**
|
|
127
|
+
`BACKLOG_FOLDER_ID = '90116192732'` in lists whose name matches `DESIGN_LIST_NAME_PATTERN`
|
|
128
|
+
(`/(?<!non-)\bdesign\b/i`). No DB/config change — these are ClickUp identifiers on
|
|
129
|
+
`_Worker_Clickup_Design`. **PR #113 (worker2) + #669 (_underscore) deploy together** as the two
|
|
130
|
+
halves of one fix; note the send() reliability change introduced **no** new shared symbol
|
|
131
|
+
(the earlier `_Exception_Api_Transient` idea was dropped — see Gotchas), so this pairing is
|
|
132
|
+
operational, not a hard undefined-symbol coupling like the FDO constants above.
|
|
133
|
+
|
|
105
134
|
## Client variations
|
|
106
135
|
None — internal TOGA Technology (`true`) design-sprint tooling.
|
|
107
136
|
|
|
@@ -158,11 +187,64 @@ None — internal TOGA Technology (`true`) design-sprint tooling.
|
|
|
158
187
|
`c25fd345`, `272f743e`, `a64c863a`, `7739cc41`, `08570f31`, `70b09962`. Keep sprint-readiness
|
|
159
188
|
automations only: `10df1786`, `f6214678`. Re-enabling any of the replaced set reintroduces the
|
|
160
189
|
duplicate+close and spurious-email behavior.
|
|
190
|
+
- **The spawn gate must admit Backlog Design lists — folder + name, not just the sprint folder
|
|
191
|
+
(ROOT CAUSE of the 2026-07-15 intermittent no-spawn).** `isDesignSprintTask()` originally accepted
|
|
192
|
+
only the True Design Sprints folder or two hardcoded `DESIGN_LIST_IDS`, so a design task living in a
|
|
193
|
+
**Backlog Design list** was silently skipped (no iteration spawned). The gate now also admits
|
|
194
|
+
`BACKLOG_FOLDER_ID = '90116192732'` when the list name matches `DESIGN_LIST_NAME_PATTERN`. **Regex
|
|
195
|
+
trap:** the pattern is `/(?<!non-)\bdesign\b/i` — a bare `\bdesign\b` wrongly matched **"Non-Design
|
|
196
|
+
Overflow"** because the hyphen is itself a word boundary, so the negative-lookbehind `(?<!non-)` is
|
|
197
|
+
required; the `\b` boundary also correctly excludes **"Redesign"**. `SendReviewReminders()` applies
|
|
198
|
+
the same folder+pattern so new backlog Design lists get reminders without a code change.
|
|
199
|
+
- **Resolve the FDO from the webhook `after` value, NOT the per-process snapshot (read-after-write
|
|
200
|
+
race — second root cause of the 2026-07-15 failure).** `handleFinalDesignOutcome` used to resolve
|
|
201
|
+
the outcome from the cached `getTaskDetails` snapshot fetched early in the request, which can
|
|
202
|
+
**predate** ClickUp reflecting the drop-down write → it read the OLD value → silent skip. The
|
|
203
|
+
webhook's authoritative new value (`history_items[].after`) is now threaded through
|
|
204
|
+
`handleFinalDesignOutcome(..., ?string $webhookFdoValue)` →
|
|
205
|
+
`resolveFinalDesignOutcomeOptionId(..., ?string $overrideValue)` (override wins over the snapshot
|
|
206
|
+
value; the option *list* is still read from the snapshot). `_Worker_Clickup::extractCustomFieldAfterValue()`
|
|
207
|
+
normalizes the `after` shape and — critically — distinguishes a **missing** `after` (→ `null` → fall
|
|
208
|
+
back to snapshot) from an **explicit null** `after` (→ `''` sentinel → field was cleared → resolve to
|
|
209
|
+
no outcome; do NOT act on the stale previous option). The previously-silent resolver-null return now
|
|
210
|
+
logs a `[ClickupDesign]` breadcrumb.
|
|
211
|
+
- **`_Component_Api_Clickup::send()` throws on transient failures — it no longer silently returns
|
|
212
|
+
NULL (shared-core, `_underscore` PR #669).** The old retry loop never actually retried and returned
|
|
213
|
+
NULL on HTTP `>= 502`, **silently dropping** the API call. Now transient failures — HTTP 502/503/504,
|
|
214
|
+
429, and transport-level errors (`responseCode === 0` / cURL failure) — **throw** so the worker job
|
|
215
|
+
fails loudly and the **queue** retries; other non-2xx still throw. Retry is intentionally left to the
|
|
216
|
+
queue (no in-worker sleep-and-retry; CTO Option C). **It throws the base `Exception`** — the same type
|
|
217
|
+
every other `_Component_Api` client throws and the framework error handler / Sentry already track — NOT
|
|
218
|
+
a bespoke exception class. (A `_Exception_Api_Transient` subclass was prototyped and then dropped
|
|
219
|
+
precisely because a new type sits outside the existing Sentry/error path.) Exception messages never
|
|
220
|
+
embed the full `$response` body (task/user PII); the private `summarizeApiError()` surfaces only
|
|
221
|
+
ClickUp `err`/`ECODE` with a bounded/truncated fallback. This affects **every** caller of `send()`
|
|
222
|
+
across the 2.0 apps — grep `_Component_Api_Clickup::send(` before assuming a caller tolerated the old
|
|
223
|
+
null-on-failure return.
|
|
161
224
|
- **Not the bug (ruled out):** `_underscore` PR #624 field/option constants were confirmed
|
|
162
225
|
present and correct — no `_underscore` change was needed. The earlier folder-gate ID-mismatch
|
|
163
|
-
suspicion was also not the cause.
|
|
226
|
+
suspicion was also not the cause. For the 2026-07-15 failure, additionally ruled out: the strict-`===`
|
|
227
|
+
resolver typing bug (already fixed & deployed on `_production`) and a cross-repo FDO-constant deploy
|
|
228
|
+
gap (all five FDO option constants confirmed present on `_underscore` `_production`).
|
|
164
229
|
|
|
165
230
|
## Change history
|
|
231
|
+
- 2026-07-15 — **Fixed intermittent "Iterated – New Task does not spawn" end-to-end (worker2 PR #113
|
|
232
|
+
+ _underscore PR #669, deploy together).** Root cause 1: `isDesignSprintTask()` skipped design tasks
|
|
233
|
+
in **Backlog Design lists** — gate now admits `BACKLOG_FOLDER_ID` + name matching
|
|
234
|
+
`DESIGN_LIST_NAME_PATTERN` (`/(?<!non-)\bdesign\b/i`; word-boundary lookbehind excludes "Non-Design"
|
|
235
|
+
and "Redesign"); `SendReviewReminders` mirrors it. Root cause 2 (read-after-write race): FDO was
|
|
236
|
+
resolved from a stale per-process `getTaskDetails` snapshot — now the webhook `history_items[].after`
|
|
237
|
+
value is threaded via `handleFinalDesignOutcome(..., $webhookFdoValue)` /
|
|
238
|
+
`resolveFinalDesignOutcomeOptionId(..., $overrideValue)` and a new
|
|
239
|
+
`_Worker_Clickup::extractCustomFieldAfterValue()` (missing `after` → snapshot fallback; explicit-null
|
|
240
|
+
→ cleared-field no-op, not stale-value action). Added `[ClickupDesign]` breadcrumbs and logging to the
|
|
241
|
+
previously-silent resolver-null and `findExistingSpawnedIteration` catch. Shared-core: hardened
|
|
242
|
+
`_Component_Api_Clickup::send()` so transient failures (502/503/504, 429, transport `responseCode 0`)
|
|
243
|
+
**throw** instead of silently returning NULL; retry left to the queue; PII-safe `summarizeApiError()`.
|
|
244
|
+
**The transient path reuses the base `Exception`** (Sentry/error-path parity) — an initially-prototyped
|
|
245
|
+
`_Exception_Api_Transient` class was dropped and removed. 55 assertions pass (Cases 6–8 added to
|
|
246
|
+
`ClickupDesignTest`); `php -l` clean. Two PHP reviews + a CTO second opinion; ruled out the (already
|
|
247
|
+
fixed) strict-`===` bug and an FDO-constant deploy gap. (ajean)
|
|
166
248
|
- 2026-07-07 (later) — **Extended the review workflow (branch
|
|
167
249
|
`feature/design-sprint-iterated-automation`, worker2 + _underscore; PRs pending manual open).**
|
|
168
250
|
Added the iterated-entry trigger `handleTaskEnteredIterated()` (FDO → In Review, then
|
package/package.json
CHANGED