toga-ai 1.0.895 → 1.0.896
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.
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
| [Background Email-Template Worker (_Worker_Notification_EmailTemplate)](features/notification-email-template.md) | Worker action that dispatches a stored client-defined `EmailTemplates` row by UUID off-thread; open for sending a complete pre-authored transactional template a |
|
|
45
45
|
| [DB-Driven Notification (Internal) Email](features/notification-email.md) | Worker action that wraps an ad-hoc subject/body in the client's DB-stored branded shell and sends it; open for internal/system notification email (merge alerts, |
|
|
46
46
|
| [NYCHH Asset-Tag Backfill (worker2)](features/nychh-asset-tag-backfill.md) | Backfills NYCHH unit asset tags + MAC addresses from NetSuite serial (inventory-number) records; open when the fulfillment sync misses a tag added to a serial a |
|
|
47
|
-
| [OneUptime Incident
|
|
47
|
+
| [OneUptime Incident → ClickUp Task Sync (Monitor/Oneuptime/SyncIncidents)](features/oneuptime-incident-clickup-sync.md) | worker2 polls OneUptime every 15 min for open incidents and creates/auto-closes a ClickUp task per incident; open for the incident→ClickUp sync, its claim-fir |
|
|
48
48
|
| [OneUptime push-metric monitors for 2.0 workers](features/oneuptime-worker2-monitoring.md) | OneUptime push-metric monitor pattern for worker2 (2.0): a self-contained cron action decides pass/fail and POSTs a string token to a OneUptime Incoming Request |
|
|
49
49
|
| [Platform Cache Cleanup (_Worker_Platform_Cache — Clean + Truncate)](features/platform-cache-cleanup.md) | `_Worker_Platform_Cache` maintenance of the shared Cache cluster behind api2 cross-client retrieval; open when touching cache reaping/truncate or the Clean cron |
|
|
50
50
|
| [QA/QC Branch Automation (auto-mirror + rebuild-from-ledger revert)](features/qa-qc-branch-automation.md) | worker2-centralized GitHub branch automation (mirror, ledger-revert, the single guarded door to `_production`); open when touching QA/QC branch merges or revert |
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: OneUptime Incident
|
|
2
|
+
title: OneUptime Incident → ClickUp Task Sync (Monitor/Oneuptime/SyncIncidents)
|
|
3
3
|
framework: "2.0"
|
|
4
4
|
repo: worker2
|
|
5
5
|
project: Worker
|
|
@@ -23,20 +23,20 @@ related:
|
|
|
23
23
|
- ../../_underscore/features/component-model-namespace-registration.md
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
worker2 polls OneUptime every 15 min for open incidents and creates/auto-closes a ClickUp task per incident; open for the incident
|
|
26
|
+
worker2 polls OneUptime every 15 min for open incidents and creates/auto-closes a ClickUp task per incident; open for the incident→ClickUp sync, its claim-first dedup, or the auto-close pass.
|
|
27
27
|
|
|
28
28
|
## Summary
|
|
29
29
|
|
|
30
|
-
An internal/shared TOGA ops feature: worker2 polls the OneUptime API every 15 minutes for **currently-open** incidents and ensures a ClickUp task exists for each, so operational incidents become actionable tickets automatically without duplicates. This is the **first *reader* of the OneUptime API** in worker2
|
|
30
|
+
An internal/shared TOGA ops feature: worker2 polls the OneUptime API every 15 minutes for **currently-open** incidents and ensures a ClickUp task exists for each, so operational incidents become actionable tickets automatically without duplicates. This is the **first *reader* of the OneUptime API** in worker2 — until now OneUptime was **push-only** (workers POST metrics/heartbeats to Incoming-Request monitors; see [OneUptime push-metric monitors](./oneuptime-worker2-monitoring.md)). The model was "dumb reporter, smart monitor"; this adds the reader half.
|
|
31
31
|
|
|
32
32
|
## How it works
|
|
33
33
|
|
|
34
34
|
### Entry points
|
|
35
35
|
|
|
36
|
-
- `worker2/Component/Api/Oneuptime/Oneuptime.php`
|
|
37
|
-
- `worker2/Worker/Monitor/Oneuptime.php`
|
|
36
|
+
- `worker2/Component/Api/Oneuptime/Oneuptime.php` — `_Component_Api_Oneuptime`, the OneUptime API read client, modeled on `_Component_Api_Togaiq`. Reads a new `[oneuptime]` config group (endpoint, api_key, project_id) via `_Config`; `isConfigured()` guard; `send()` wraps `_ApiRequest` with `APIKey` + `ProjectID` headers; `getActiveIncidents()` returns normalized open incidents (filters out resolved **in PHP**).
|
|
37
|
+
- `worker2/Worker/Monitor/Oneuptime.php` — `_Worker_Monitor_Oneuptime::SyncIncidents(): string`, the cron action. Returns a structured summary string on every path (`incidentsPolled` / `created` / `skipped` / `failed`, plus `closed` / `closeFailed`).
|
|
38
38
|
|
|
39
|
-
Canonical worker2 scheduled-3rd-party-API-job shape (see [Creating Worker Actions](./creating-worker-actions.md)): a `Core.CronJobs` row (cron expr, Central tz)
|
|
39
|
+
Canonical worker2 scheduled-3rd-party-API-job shape (see [Creating Worker Actions](./creating-worker-actions.md)): a `Core.CronJobs` row (cron expr, Central tz) → CronScheduler Lambda enqueues → `Controller/Index::worker()` dispatches action `Monitor/Oneuptime/SyncIncidents` to `_Worker_Monitor_Oneuptime::SyncIncidents`. HTTP goes through `_ApiRequest`; secrets come from `_Config::oneuptime()` reading `Config/<env>.ini`; ClickUp uses `_Component_Api_Clickup::send()`.
|
|
40
40
|
|
|
41
41
|
### Open-only
|
|
42
42
|
|
|
@@ -44,14 +44,14 @@ Only currently-open incidents are synced. The list route returns all incidents a
|
|
|
44
44
|
|
|
45
45
|
### Claim-first (unique-key) dedup
|
|
46
46
|
|
|
47
|
-
For each open incident, INSERT a ledger row into `OneUptimeIncidentTasks` keyed on the OneUptime incident id **before** calling ClickUp. A unique-key violation means a task already exists
|
|
47
|
+
For each open incident, INSERT a ledger row into `OneUptimeIncidentTasks` keyed on the OneUptime incident id **before** calling ClickUp. A unique-key violation means a task already exists → skip. On success, create the ClickUp task via `_Component_Api_Clickup::send('POST','/list/{listId}/task', …)` and attach the returned id to the ledger row; on failure, **delete the claim and re-throw**. Established worker2 way to prevent duplicate ClickUp tasks under overlapping cron runs — pattern copied from `_Worker_Infrastructure_Errors::createClickupTask`. `INSERT … WHERE NOT EXISTS` is **insufficient** under REPEATABLE READ; the UNIQUE key is the concurrency arbiter.
|
|
48
48
|
|
|
49
49
|
### Close pass (auto-close on incident resolution)
|
|
50
50
|
|
|
51
51
|
The sync was originally **create-only**; a ClickUp task opened for an incident was never closed once the incident resolved, so a recurrence (a *new* OneUptime incident with the same monitor title) opened a second task that **looked like a duplicate** but was not. `SyncIncidents()` now runs a **close pass first** each cycle, independent of how many incidents are currently open, via `closeResolvedIncidents()`:
|
|
52
52
|
|
|
53
53
|
- SELECT ledger rows `WHERE clickupIdentifier IS NOT NULL AND dtClosed IS NULL`.
|
|
54
|
-
- For each, call `_Component_Api_Oneuptime::getIncidentResolvedState(string $incidentId): ?bool`
|
|
54
|
+
- For each, call `_Component_Api_Oneuptime::getIncidentResolvedState(string $incidentId): ?bool` — `true` = resolved, `false` = confirmed still open, `null` = unknown. **Only a definite `true` closes a task**; `null` means "leave alone" so an ambiguous read never closes a live incident's ticket. Uses a new per-incident route (`ROUTE__INCIDENT_ITEM`, `/api/incident/{id}/get-item`, still a PLACEHOLDER) because resolved incidents no longer appear in `getActiveIncidents()` and must be re-checked individually. A 2xx response missing `currentIncidentState.isResolvedState` logs a warning (incident id only, no body/PII) so a wrong placeholder shape is detectable rather than silently returning `null` forever.
|
|
55
55
|
- To close, PUT `/task/{id}` with the list's **closed-type status NAME** (ClickUp has no generic close verb), resolved per-list via `GET /list/{id}` `statuses[]` where `type == 'closed'`, cached per run in `$closedStatusByList`. Then stamp `dtClosed = NOW()`.
|
|
56
56
|
- `dtClosed` is stamped **only after a successful close**, so a failure retries next run. Per-incident failures are caught (`Throwable`) + `error_log` + counted, not fatal; whole-run failures (e.g. no sprint list) still re-throw for queue retry.
|
|
57
57
|
- All new ClickUp calls in the close path are throttled with `usleep(_Worker_Clickup_Project::RATE_LIMIT_DELAY_US)` (120ms = 500/min), matching `_Worker_Infrastructure_Errors`.
|
|
@@ -60,36 +60,36 @@ Helpers added: `closeResolvedIncidents()`, `closeClickupTask()`, `resolveClosedS
|
|
|
60
60
|
|
|
61
61
|
### Target list = current sprint list
|
|
62
62
|
|
|
63
|
-
The ClickUp task lands in the **current sprint** list, resolved by `GET /folder/{id}` and matching the list whose `start_date`/`due_date` window contains now (mirrors `_Worker_Team_Sprint::SprintLock`
|
|
63
|
+
The ClickUp task lands in the **current sprint** list, resolved by `GET /folder/{id}` and matching the list whose `start_date`/`due_date` window contains now (mirrors `_Worker_Team_Sprint::SprintLock` — see [Team Sprint Management](./team-sprint-management.md) and the sprint-promotion note in [Error Escalation Cron](./error-escalation-cron.md)). Folder id is read from `[clickup] sprint_folder_id` with a constant fallback (`90116585845 — Dev Sprints; repointed by TRUE-82052 on 2026-09-21`).
|
|
64
64
|
|
|
65
65
|
### Data model
|
|
66
66
|
|
|
67
|
-
`OneUptimeIncidentTasks` (CREATE on the **Team** cluster
|
|
67
|
+
`OneUptimeIncidentTasks` (CREATE on the **Team** cluster — chosen over Logs; `dbchanges2/Team/2026-08-10a`): `id`, `uuid char(36)`, `dtCreated`, `oneuptimeIncidentId varchar(64)`, `clickupIdentifier varchar(64) NULL`, `dtClosed DATETIME NULL` (added `dbchanges2/Team/2026-08-12a`, `AFTER clickupIdentifier`). The **UNIQUE KEY on `oneuptimeIncidentId`** is the dedup contract, same rationale as `IssueClickupTasks`. `dtClosed` marks a ledger row whose ClickUp task has been closed following incident resolution (NULL = still open / not yet closed).
|
|
68
68
|
|
|
69
69
|
### Provisioning / deploy
|
|
70
70
|
|
|
71
71
|
- `dbchanges2/Core/2026-08-10b` inserts the `Core.CronJobs` row: action `Monitor/Oneuptime/SyncIncidents`, schedule `*/15 * * * *`, `maxExecutionTime` 120, seeded **`isActive = 0`**. Flip to 1 only after (a) the ledger table exists, (b) the `[oneuptime]` config group is populated, (c) the incident-list route is confirmed.
|
|
72
|
-
- `[oneuptime]` config group (endpoint, api_key, project_id) added to `worker2/Config/production.ini` with placeholders
|
|
72
|
+
- `[oneuptime]` config group (endpoint, api_key, project_id) added to `worker2/Config/production.ini` with placeholders — **values filled by the developer, not committed**. Credentials live only in the ini files.
|
|
73
73
|
|
|
74
74
|
### Configuration / credentials
|
|
75
75
|
|
|
76
|
-
- `[oneuptime]`
|
|
77
|
-
- `[clickup]`
|
|
76
|
+
- `[oneuptime]` — `endpoint`, `api_key`, `project_id`. The hosted OneUptime SaaS base is `https://oneuptime.com` (confirmed from `worker/.ebextensions/045_oneuptime_agent.config`); the REST API path is appended to that base.
|
|
77
|
+
- `[clickup]` — `sprint_folder_id` (fallback constant `90116585845 — Dev Sprints; repointed by TRUE-82052 on 2026-09-21`) plus the ClickUp token. ClickUp/OneUptime token **values** live in `worker2/Config/<env>.ini` — location only, never the value.
|
|
78
78
|
|
|
79
79
|
## Gotchas
|
|
80
80
|
|
|
81
81
|
- **Same-title ClickUp tickets from OneUptime are expected on incident recurrence.** Dedup is **per-incident-id, not per-title**: the `oneuptimeIncidentId` UNIQUE key + claim-first insert only prevents duplicates for the *same* incident id. Two tasks with the same monitor title minutes apart are two *different* OneUptime incidents, not a dedup failure. The symptom that looked like duplication was actually the missing auto-close: incident #1 resolved but its task never closed, then a fresh incident opened a second task. Auto-close closes #1 on resolution so a recurrence (#2) is the only open ticket.
|
|
82
|
-
- **ClickUp has no generic "close" verb.** A task is closed by PUTting the exact NAME of the status whose `type == 'closed'`, which is **list-specific**
|
|
82
|
+
- **ClickUp has no generic "close" verb.** A task is closed by PUTting the exact NAME of the status whose `type == 'closed'`, which is **list-specific** — resolved per-list from `GET /list/{id}` `statuses[]` and cached per run.
|
|
83
83
|
- Throttle every ClickUp call in the close path with `usleep(RATE_LIMIT_DELAY_US)` (120ms) or rate-limit failures degrade into `closeFailed` counts.
|
|
84
|
-
- **Both OneUptime routes remain PLACEHOLDERS**
|
|
85
|
-
- **OneUptime cannot filter open-only server-side here**
|
|
84
|
+
- **Both OneUptime routes remain PLACEHOLDERS** — the incident-list route (`/api/incident/get-list`) *and* the new per-incident route (`/api/incident/{id}/get-item`, `ROUTE__INCIDENT_ITEM`) are unverified against the live OneUptime API. Auto-close **fails safe** (only a definite resolved `true` closes anything; `null` leaves the task alone) but won't fire until the route/field shape is confirmed. Do not enable the cron (`isActive = 1`) until the routes and returned fields are verified.
|
|
85
|
+
- **OneUptime cannot filter open-only server-side here** — resolution filtering happens in PHP, so the returned field set must actually carry the resolved/open state.
|
|
86
86
|
- Ship the worker2 code deploy and both dbchanges2 migrations **together**; the cron row is inert until the action is deployed and the ledger table exists.
|
|
87
|
-
- **Deploy order for the auto-close change:** run `dbchanges2/Team/2026-08-12a` (the `dtClosed` column) **before** the worker2 code deploys
|
|
87
|
+
- **Deploy order for the auto-close change:** run `dbchanges2/Team/2026-08-12a` (the `dtClosed` column) **before** the worker2 code deploys — the new close-pass queries reference `dtClosed`.
|
|
88
88
|
|
|
89
89
|
## Related
|
|
90
90
|
|
|
91
|
-
- [OneUptime push-metric monitors](./oneuptime-worker2-monitoring.md)
|
|
92
|
-
- [Error Escalation Cron](./error-escalation-cron.md)
|
|
93
|
-
- [Team Sprint Management](./team-sprint-management.md)
|
|
94
|
-
- [Creating Worker Actions](./creating-worker-actions.md)
|
|
91
|
+
- [OneUptime push-metric monitors](./oneuptime-worker2-monitoring.md) — the push-only reporter half.
|
|
92
|
+
- [Error Escalation Cron](./error-escalation-cron.md) — the `_Worker_Infrastructure_Errors` claim-first pattern this copies, and sprint promotion.
|
|
93
|
+
- [Team Sprint Management](./team-sprint-management.md) — the current-sprint-list resolution mirrored here.
|
|
94
|
+
- [Creating Worker Actions](./creating-worker-actions.md) — the scheduled-3rd-party-API-job shape.
|
|
95
95
|
- [Component/model namespace registration](../../_underscore/features/component-model-namespace-registration.md).
|
package/package.json
CHANGED