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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.187",
3
+ "version": "1.0.189",
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",