toga-ai 1.0.187 → 1.0.189
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.
|
@@ -193,9 +193,30 @@ backfills `workTypeAtLock` the first time a task is seen).
|
|
|
193
193
|
and the committed values should be rotated. See team coding/security standards.
|
|
194
194
|
- **Worker contract.** Like all 2.0 workers this must return cleanly and commit writes via
|
|
195
195
|
the `_Db`/transaction layer to be durable — uncommitted captures can be silently dropped.
|
|
196
|
+
- **ClickUp pagination returns duplicate tasks — the sync loop must be idempotent.** ClickUp's
|
|
197
|
+
page-based task API (`/list/{id}/task?page=N`) can return the **same task on more than one
|
|
198
|
+
page**, especially on large/long runs (a real failure ran ~418s across many pages). If the
|
|
199
|
+
sync loop treats a re-seen task as new, it builds a second `_Model_Team_Task` (id=NULL) and
|
|
200
|
+
`save()`s it → a second INSERT → `Duplicate entry '<sprintId>-<clickupId>' for key
|
|
201
|
+
'Tasks.sprintId_clickupIdentifier'` and the whole run aborts. **Design rule (the two-map
|
|
202
|
+
pattern):** keep two separate maps keyed by `clickupIdentifier`. `$lookupTaskByClickUpId`
|
|
203
|
+
drives update-vs-insert and **must stay current for the entire run** — register every
|
|
204
|
+
newly-created task into it immediately and never `unset()` it mid-loop, so a duplicate page
|
|
205
|
+
row resolves to the same model and does an UPDATE. A separate `$tasksToDelete` map (seeded
|
|
206
|
+
from existing tasks) has entries removed as each task is seen; whatever remains after the
|
|
207
|
+
loop is deleted (SprintLock) / archived to `statusNow = ARCHIVED` (the Capture methods).
|
|
208
|
+
Do **not** overload one map for both jobs. This pattern is replicated identically in
|
|
209
|
+
`SprintLock()`, `CaptureSprintEnd()`, and `CaptureSprintDaily()` — fix all three together.
|
|
196
210
|
|
|
197
211
|
## Change history
|
|
198
212
|
|
|
213
|
+
- 2026-06-24 — Fixed a duplicate-key crash (`Tasks.sprintId_clickupIdentifier`) in the
|
|
214
|
+
ClickUp→DB sync: ClickUp pagination can return the same task on multiple pages, and the
|
|
215
|
+
loop was overloading one map (`$lookupTaskByClickUpId`) for both update-vs-insert and
|
|
216
|
+
deletion tracking, `unset()`ing entries mid-loop so a re-seen task was treated as new and
|
|
217
|
+
re-INSERTed. Decoupled into two maps — a persistent `$lookupTaskByClickUpId` (new tasks
|
|
218
|
+
registered immediately, never unset) and a separate `$tasksToDelete` — making re-processing
|
|
219
|
+
idempotent. Applied to `SprintLock()`, `CaptureSprintEnd()`, and `CaptureSprintDaily()`. (jcardinal)
|
|
199
220
|
- 2026-06-24 — Initial documentation of `_Worker_Team_Sprint` (`Worker/Team/Sprint.php`):
|
|
200
221
|
the sprint lifecycle (warnings → lock → daily → end → release notes), the four-metric
|
|
201
222
|
developer scoring model (reliability/quality/hygiene/integrity with weights, team-relative
|
package/package.json
CHANGED