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-06-23
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-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-07):** the silent no-op fix is **shipped and live**
28
- worker2 PR #98 was merged to `_production` (commit `7a36564`); the type-tolerant resolver
29
- and the ClickBot email guard are now in production. On top of that, the review workflow was
30
- extended: entering the **"iterated"** status now auto-drives the review state and iteration
31
- count, `handleFinalDesignOutcome` now handles **all five** FDO options explicitly, and the
32
- spawned duplicate carries over more fields. This new work is on branch
33
- `feature/design-sprint-iterated-automation` in **both** worker2 and _underscore (committed +
34
- pushed; PRs to be opened manually must deploy together, see Data model). The native ClickUp
35
- automations that were masking the no-op must stay disabled see Gotchas.
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(...)` — the entry that decides spawn vs. complete; now branches
40
- on **all five** FDO options explicitly (no silent fall-through — see How it works).
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
- - `TRUE_DESIGN_SPRINTS_FOLDER_ID` constant (Design.php:27,50).
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.347",
3
+ "version": "1.0.348",
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",