toga-ai 1.0.274 → 1.0.276
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.
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
| [Worker (worker2) Architecture](architecture.md) | Worker (repo `worker2`) is an AWS Elastic Beanstalk **Worker Tier** application that processes background jobs. | worker2/Controller/Index.php, worker2/Worker/, worker2/LambdaFunctions/, _underscore/Worker.php |
|
|
6
6
|
| [Automated PR Merger — Concurrent Force-Push Clobber Race](features/automated-pr-merger-force-push-race.md) | The automated PR merger `_Worker_Team_GitHub::Merge` (`worker2` `Worker/Team/Github.php`) merges approved PRs to `_production` by **force-pushing from a clone t | Worker/Team/Github.php |
|
|
7
7
|
| [ClickUp Connectivity Watchdog](features/clickup-connectivity-watchdog.md) | A cron watchdog that emails when the ClickUp integration looks disconnected during business hours. | worker2/Worker/Clickup/Health.php, worker2/Database/ClickupHealthWatchdog.sql |
|
|
8
|
-
| [ClickUp Design Sprint Automation (Final Design Outcome)](features/clickup-design-sprint-automation.md) | `_Worker_Clickup_Design` is meant to drive the design-sprint workflow in ClickUp via the API, replacing a set of native ClickUp automations. | worker2/Worker/Clickup/Design.php, worker2/Worker/Clickup.php |
|
|
8
|
+
| [ClickUp Design Sprint Automation (Final Design Outcome)](features/clickup-design-sprint-automation.md) | `_Worker_Clickup_Design` is meant to drive the design-sprint workflow in ClickUp via the API, replacing a set of native ClickUp automations. | worker2/Worker/Clickup/Design.php, worker2/Worker/Clickup.php, worker2/Controller/ClickupDesignTest.php, _underscore/Component/Api/Clickup/Clickup.php |
|
|
9
9
|
| [ClickUp GitHub-tab Auto-linking & Ticket-id Branch Naming](features/clickup-github-autolink.md) | How ClickUp surfaces branches/PRs/commits in a ticket's **GitHub tab**, and the branch / PR-title naming convention that triggers it. | |
|
|
10
10
|
| [ClickUp Project & Opportunity Multi-List Routing](features/clickup-project-routing.md) | Routes ClickUp tasks into the correct **secondary multi-list memberships** based on their custom-field values, via the `clickup` webhook. | worker2/Worker/Clickup/Project.php, worker2/Worker/Clickup.php |
|
|
11
11
|
| [ClickUp Rich-Text Custom Fields via Quill Delta (API)](features/clickup-richtext-api.md) | ClickUp custom text fields (type `text` and long-text) support rich formatting only through a **Quill Delta** written to the undocumented `value_richtext` key o | test/@dave/clickup_md2delta.js, .claude/skills/plan-ticket/scripts/clickup.js |
|
|
@@ -6,11 +6,13 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-07-
|
|
9
|
+
updated: 2026-07-07
|
|
10
10
|
owners: ["ajean"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Clickup/Design.php
|
|
13
13
|
- worker2/Worker/Clickup.php
|
|
14
|
+
- worker2/Controller/ClickupDesignTest.php
|
|
15
|
+
- _underscore/Component/Api/Clickup/Clickup.php
|
|
14
16
|
related:
|
|
15
17
|
- ./clickup-work-type-automation.md
|
|
16
18
|
- ./team-sprint-management.md
|
|
@@ -22,87 +24,170 @@ replacing a set of native ClickUp automations. When a task's **Final Design Outc
|
|
|
22
24
|
drop-down is set to an actionable value, the worker either spawns an iteration task or
|
|
23
25
|
completes the task, and a daily cron reminds the Design Approver of pending reviews.
|
|
24
26
|
|
|
25
|
-
**
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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.
|
|
30
36
|
|
|
31
37
|
## Key files / entry points
|
|
32
38
|
- `Worker/Clickup/Design.php` → `_Worker_Clickup_Design` — the design-sprint engine.
|
|
33
|
-
- `handleFinalDesignOutcome(...)` — the entry that decides spawn vs. complete
|
|
34
|
-
silent
|
|
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
|
+
- `handleTaskEnteredIterated()` — **new**; fired when a task ENTERS "iterated" status. Sets
|
|
42
|
+
FDO = "In Review" then increments the Iteration custom field by +1 (idempotent — see
|
|
43
|
+
Gotchas). Folder-gated via `isInDesignSprintsFolder()`.
|
|
44
|
+
- `isInDesignSprintsFolder(...)` — **new** private folder-gate helper (reused by the
|
|
45
|
+
iterated-entry trigger).
|
|
35
46
|
- `resolveFinalDesignOutcomeOptionId(...)` (Design.php:298-317) — maps the drop-down value
|
|
36
|
-
to an option ID.
|
|
37
|
-
- `spawnIterationTask(...)` / `
|
|
38
|
-
Design Approver
|
|
39
|
-
|
|
47
|
+
to an option ID; now **string-compares** value/orderindex/id (see Gotchas).
|
|
48
|
+
- `spawnIterationTask(...)` / `buildIterationCreatePayload(...)` / `carryDesignApprover(...)` /
|
|
49
|
+
`carryComments(...)` (**new**) — duplicate the task and copy description, Design Approver,
|
|
50
|
+
and now all user comments; the duplicate no longer bumps Iteration (moved to the
|
|
51
|
+
iterated-entry trigger) and is set to FDO = "In Progress" post-create.
|
|
52
|
+
- `completeTask(...)` — closes an approved / cancelled task (also used on iterated → complete).
|
|
40
53
|
- `SendReviewReminders(...)` — daily reminder cron action.
|
|
41
54
|
- `TRUE_DESIGN_SPRINTS_FOLDER_ID` constant (Design.php:27,50).
|
|
42
|
-
- `Worker/Clickup.php`
|
|
43
|
-
|
|
55
|
+
- `Worker/Clickup.php` `Webhook` — the `switch ($historyItem->after->status)` block (see
|
|
56
|
+
Gotchas re: after vs. before) now has a `case _Worker_Clickup_Design::STATUS_ITERATED`
|
|
57
|
+
dispatching to `handleTaskEnteredIterated()`.
|
|
58
|
+
- `Worker/Clickup.php` — routes ClickUp webhooks.
|
|
59
|
+
- `shouldEmailDesignApprover($payload, $taskDetails)` — the single predicate gating the
|
|
60
|
+
"Action Needed: Design Review" email (requires history_items[0] is a Design Approver
|
|
61
|
+
custom_field change, task status still `iterated`, and user-driven: `user.id != -1`).
|
|
62
|
+
- `taskUpdated` path (~822) and `taskStatusUpdated` path (~230) both gate the email on this
|
|
63
|
+
predicate.
|
|
64
|
+
- `Controller/ClickupDesignTest.php` — regression tests reached via the
|
|
65
|
+
`?controller=ClickupDesignTest` route (worker2 has **no PHPUnit harness**; mirrors the
|
|
66
|
+
`CompassPeopleTest` convention). Uses `ReflectionMethod` to reach the private methods.
|
|
44
67
|
|
|
45
68
|
## How it works
|
|
46
69
|
1. A ClickUp webhook reaches `_Worker_Clickup`; the dispatcher (Clickup.php ~822-875) routes
|
|
47
70
|
design-folder events into `_Worker_Clickup_Design`.
|
|
48
|
-
2.
|
|
49
|
-
- **"Iterated – New Task"**
|
|
50
|
-
|
|
51
|
-
- **"Approved"**
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
2. `handleFinalDesignOutcome` now branches on **all five** FDO options explicitly:
|
|
72
|
+
- **"Iterated – New Task"** → `spawnIterationTask` (duplicate) **then** `completeTask` on
|
|
73
|
+
the source.
|
|
74
|
+
- **"Approved"** → `completeTask`.
|
|
75
|
+
- **"Cancelled"** → `completeTask` (terminal; newly handled — Cancelled now closes the task).
|
|
76
|
+
- **"In Progress" / "In Review"** → explicit no-op.
|
|
77
|
+
- unrecognized → logged (no more silent implicit-else fall-through).
|
|
78
|
+
3. **Iterated-entry trigger (new):** when a True Design Sprints task ENTERS the "iterated"
|
|
79
|
+
status, `handleTaskEnteredIterated()` sets FDO = **"In Review"** and increments the
|
|
80
|
+
**Iteration** custom field by +1. This is what now drives the review state + round count
|
|
81
|
+
on review submission; previously nothing fired on entering "iterated". Folder-gated.
|
|
82
|
+
4. **Duplicate field carry-over:** the spawned iteration task copies description, Design
|
|
83
|
+
Approver, and **all user comments** (`carryComments()` — GET `/task/{id}/comment`,
|
|
84
|
+
re-posted oldest-first with `[Original by <user> on <date>]` attribution, non-fatal). It
|
|
85
|
+
copies **Iteration unchanged** (omitted if the source has none — the +1 now lives only on
|
|
86
|
+
the iterated-entry trigger, so a review round is never double-counted) and is set to
|
|
87
|
+
FDO = **"In Progress"** post-create (POST `/task/{id}/field/{id}` drop_down pattern).
|
|
58
88
|
5. Daily reminder cron `Core.CronJobs` id 21 ("Design Review Reminders", `0 9 * * 1-5`,
|
|
59
89
|
action `Clickup/Design/SendReviewReminders`) nudges the approver about pending reviews.
|
|
60
90
|
|
|
61
91
|
## Data model
|
|
62
92
|
Configuration lives as ClickUp identifiers (folder IDs, custom-field IDs, drop-down option
|
|
63
93
|
UUIDs), not in the DB. The custom-field / option-ID constants are defined in `_underscore`
|
|
64
|
-
(`
|
|
65
|
-
|
|
94
|
+
(`_Component_Api_Clickup`, `_underscore/Component/Api/Clickup/Clickup.php`) — confirmed
|
|
95
|
+
present (worker2 PR #80, _underscore PR #624).
|
|
96
|
+
|
|
97
|
+
**New FDO option constants (this session):** `FINAL_DESIGN_OUTCOME__IN_PROGRESS`,
|
|
98
|
+
`FINAL_DESIGN_OUTCOME__IN_REVIEW`, and `FINAL_DESIGN_OUTCOME__CANCELLED` were added to
|
|
99
|
+
`_Component_Api_Clickup` (previously only `..._ITERATED_NEW_TASK` and `..._APPROVED` existed).
|
|
100
|
+
**Cross-repo coupling:** worker2 references these at runtime, so `_underscore` must deploy
|
|
101
|
+
**together with** the worker2 change — deploying worker2 alone against an old _underscore
|
|
102
|
+
throws undefined-constant. Both changes are on branch
|
|
103
|
+
`feature/design-sprint-iterated-automation`.
|
|
66
104
|
|
|
67
105
|
## Client variations
|
|
68
106
|
None — internal TOGA Technology (`true`) design-sprint tooling.
|
|
69
107
|
|
|
70
108
|
## Gotchas / known issues
|
|
71
|
-
- **
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
the
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
`
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
- **The ClickUp API auth is a single hard-coded personal token — a dead token silently kills
|
|
110
|
+
ALL ClickUp automation.** The integration authenticates via one committed class constant,
|
|
111
|
+
`_Component_Api_Clickup::ACCESS_TOKEN` (in `_underscore/Component/Api/Clickup/Clickup.php`),
|
|
112
|
+
identical on `_production` and feature branches. This is a known secret-in-committed-code
|
|
113
|
+
risk. When the token is revoked/expired, `GET /user` and `GET /task` return **HTTP 401
|
|
114
|
+
"Oauth token not found" / error `OAUTH_019`**; `getTaskDetails` then throws and **every**
|
|
115
|
+
Clickup/Webhook worker job fails — presenting as "the automation silently does nothing."
|
|
116
|
+
This actually happened mid-session on 2026-07-07 (~09:49). **Fix:** regenerate the token in
|
|
117
|
+
ClickUp, update the `ACCESS_TOKEN` constant, and deploy `_underscore`. (Do not record the
|
|
118
|
+
token value anywhere.)
|
|
119
|
+
- **Iterated-entry trigger is idempotent — order matters.** `handleTaskEnteredIterated()`
|
|
120
|
+
writes **FDO = In Review FIRST** as the marker, THEN increments Iteration. A re-delivered /
|
|
121
|
+
retried webhook sees FDO already "In Review" and skips, so **Iteration can never
|
|
122
|
+
compound-double-increment**. Worst case is a single missed +1, visible as a failed
|
|
123
|
+
WorkerJob. If you reorder these two writes you reintroduce the double-count risk.
|
|
124
|
+
- **`taskStatusUpdated` dispatch structure (route-safety facts).** The
|
|
125
|
+
`switch ($historyItem->after->status)` block sits **OUTSIDE** the `if (!is_null($sprintNumber))`
|
|
126
|
+
guard, so entering-status handlers fire even for design tasks with no "sprint N" tag —
|
|
127
|
+
**each case must self-guard on `sprintNumber`** (the individual cases do). No webhook loop:
|
|
128
|
+
custom-field writes emit `taskUpdated` (not `taskStatusUpdated`) and land on the FDO no-op
|
|
129
|
+
cases; the Iteration write's custom_field is named "Iteration" so it never re-enters the FDO
|
|
130
|
+
dispatch gate.
|
|
131
|
+
- **Never use strict `===` when reading a ClickUp `drop_down` value/orderindex/id (ROOT CAUSE
|
|
132
|
+
of the silent no-op).** ClickUp returns `value`/`orderindex` with **inconsistent int-vs-string
|
|
133
|
+
typing** (`"2" !== 2`), so `resolveFinalDesignOutcomeOptionId` matched nothing, returned
|
|
134
|
+
`null`, and `handleFinalDesignOutcome` hit its early return (job still `isSuccess=1`). Fixed
|
|
135
|
+
by comparing **as strings**: `(string)$option->orderindex === (string)$field->value` (and the
|
|
136
|
+
same for `$option->id`). Note that **every other** drop_down reader in `Worker/Clickup.php`
|
|
137
|
+
relies on PHP **array-key coercion** (`$optionsByIndex[$option->orderindex]` then indexed by
|
|
138
|
+
`$customField->value`) precisely because of this typing inconsistency — strict `===` anywhere
|
|
139
|
+
reading a ClickUp drop_down value is a bug. The native automation `c25fd345` had been masking
|
|
140
|
+
this no-op in prod.
|
|
141
|
+
- **`taskStatusUpdated` switches on `before->status`, NOT `after->status`** (Clickup.php ~71).
|
|
142
|
+
A `case '<status>'` in that switch therefore fires when a task **LEAVES** that status — the
|
|
143
|
+
in-code comment claiming "just changed TO iterated" is misleading. This is the trap behind the
|
|
144
|
+
second spurious-email vector: the `iterated` case fired on `completeTask()`'s
|
|
145
|
+
`iterated → complete` transition during `spawnIterationTask`. Anyone adding status-transition
|
|
146
|
+
logic must account for this direction. The "entering iterated" trigger added this session is
|
|
147
|
+
correctly placed under `switch ($historyItem->after->status)` (`case ...::STATUS_ITERATED` →
|
|
148
|
+
`handleTaskEnteredIterated()`) — distinct from the `before->status` switch.
|
|
149
|
+
- **Spurious "Design Review" email — fixed on a single predicate.** Both vectors (the
|
|
150
|
+
`taskUpdated` Design-Approver-field path ~822 and the `taskStatusUpdated` `iterated` path
|
|
151
|
+
~230) now gate on `shouldEmailDesignApprover($payload, $taskDetails)`, which requires:
|
|
152
|
+
`history_items[0]` is a Design Approver custom_field change, task status still `iterated`, and
|
|
153
|
+
the change is **user-driven** (`user.id != -1`; ClickBot/automation writes carry
|
|
154
|
+
`user.id == -1`). The dead `$isDesignApproverSet` block was removed. Consequence: a pure status
|
|
155
|
+
change **into** iterated (approver already set, no field change) no longer emails from the
|
|
156
|
+
status path — the legitimate review email flows from the `taskUpdated` field handler.
|
|
157
|
+
- **Native automations must stay disabled once the worker is verified.** The worker replaces:
|
|
158
|
+
`c25fd345`, `272f743e`, `a64c863a`, `7739cc41`, `08570f31`, `70b09962`. Keep sprint-readiness
|
|
159
|
+
automations only: `10df1786`, `f6214678`. Re-enabling any of the replaced set reintroduces the
|
|
160
|
+
duplicate+close and spurious-email behavior.
|
|
161
|
+
- **Not the bug (ruled out):** `_underscore` PR #624 field/option constants were confirmed
|
|
162
|
+
present and correct — no `_underscore` change was needed. The earlier folder-gate ID-mismatch
|
|
163
|
+
suspicion was also not the cause.
|
|
104
164
|
|
|
105
165
|
## Change history
|
|
166
|
+
- 2026-07-07 (later) — **Extended the review workflow (branch
|
|
167
|
+
`feature/design-sprint-iterated-automation`, worker2 + _underscore; PRs pending manual open).**
|
|
168
|
+
Added the iterated-entry trigger `handleTaskEnteredIterated()` (FDO → In Review, then
|
|
169
|
+
Iteration +1; idempotent, folder-gated via new `isInDesignSprintsFolder()`), wired via a new
|
|
170
|
+
`case ...::STATUS_ITERATED` under `switch ($historyItem->after->status)` in `Clickup::Webhook`.
|
|
171
|
+
Moved the Iteration +1 OFF the spawn path (`buildIterationCreatePayload`) so the duplicate
|
|
172
|
+
copies Iteration unchanged and review rounds are not double-counted; the duplicate now also
|
|
173
|
+
sets FDO = In Progress post-create and carries over all user comments (`carryComments()`) plus
|
|
174
|
+
the existing description + Design Approver. `handleFinalDesignOutcome` now handles all five FDO
|
|
175
|
+
options explicitly (Cancelled now closes the task; In Progress/In Review are explicit no-ops;
|
|
176
|
+
unrecognized is logged). Added FDO option constants `__IN_PROGRESS`, `__IN_REVIEW`,
|
|
177
|
+
`__CANCELLED` to `_Component_Api_Clickup` in _underscore (cross-repo coupling — deploy
|
|
178
|
+
together). **Operational:** discovered the ClickUp `ACCESS_TOKEN` is a single committed
|
|
179
|
+
constant and was revoked mid-session (HTTP 401 / `OAUTH_019`), taking all ClickUp automation
|
|
180
|
+
down — documented the symptom and fix (regenerate + redeploy _underscore) as a gotcha. PR #98
|
|
181
|
+
is now MERGED to `_production` (commit `7a36564`) and live. (ajean)
|
|
182
|
+
- 2026-07-07 — Fixed and shipped the silent no-op (worker2 PR #98, branch
|
|
183
|
+
`fix/clickup-design-outcome-resolver`). Confirmed root cause: strict `===` on ClickUp
|
|
184
|
+
`drop_down` value/orderindex/id in `resolveFinalDesignOutcomeOptionId` (inconsistent int/string
|
|
185
|
+
typing) — now string-compared. Fixed the spurious "Design Review" email (both the `taskUpdated`
|
|
186
|
+
and `taskStatusUpdated` vectors) behind a single `shouldEmailDesignApprover` predicate that
|
|
187
|
+
requires a user-driven Design-Approver field change on an `iterated` task. Documented that
|
|
188
|
+
`taskStatusUpdated` switches on `before->status` (fires on LEAVING a status). Added regression
|
|
189
|
+
tests in `Controller/ClickupDesignTest.php` (run via `?controller=ClickupDesignTest`). Ruled
|
|
190
|
+
out `_underscore` PR #624 constants as the cause. (ajean)
|
|
106
191
|
- 2026-07-06 — Diagnosed the "design-sprint API automation not running" report (TRUE-79685;
|
|
107
192
|
tests TRUE-80026/80046): `handleFinalDesignOutcome` is silently no-opping in prod, masked by
|
|
108
193
|
never-disabled native automation `c25fd345`. Narrowed to two suspects (folder-gate ID
|
package/package.json
CHANGED