toga-ai 1.0.558 → 1.0.560
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.
package/.claude/settings.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"hooks": [
|
|
6
6
|
{
|
|
7
7
|
"type": "command",
|
|
8
|
-
"command": "node \"
|
|
8
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/kickoff-gate.js\"",
|
|
9
9
|
"timeout": 3000
|
|
10
10
|
}
|
|
11
11
|
]
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"hooks": [
|
|
18
18
|
{
|
|
19
19
|
"type": "command",
|
|
20
|
-
"command": "node \"
|
|
20
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/kickoff-gate.js\"",
|
|
21
21
|
"timeout": 3000
|
|
22
22
|
}
|
|
23
23
|
]
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"hooks": [
|
|
28
28
|
{
|
|
29
29
|
"type": "command",
|
|
30
|
-
"command": "node \"
|
|
30
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/block-destructive.js\"",
|
|
31
31
|
"timeout": 3000
|
|
32
32
|
}
|
|
33
33
|
]
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"hooks": [
|
|
38
38
|
{
|
|
39
39
|
"type": "command",
|
|
40
|
-
"command": "node \"
|
|
40
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/guard-index.js\"",
|
|
41
41
|
"timeout": 3000
|
|
42
42
|
}
|
|
43
43
|
]
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"hooks": [
|
|
48
48
|
{
|
|
49
49
|
"type": "command",
|
|
50
|
-
"command": "node \"
|
|
50
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/dbchanges2-cluster-isolation.js\"",
|
|
51
51
|
"timeout": 3000
|
|
52
52
|
}
|
|
53
53
|
]
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"hooks": [
|
|
58
58
|
{
|
|
59
59
|
"type": "command",
|
|
60
|
-
"command": "node \"
|
|
60
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/dbchanges2-record-ids.js\"",
|
|
61
61
|
"timeout": 3000
|
|
62
62
|
}
|
|
63
63
|
]
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"hooks": [
|
|
70
70
|
{
|
|
71
71
|
"type": "command",
|
|
72
|
-
"command": "node \"
|
|
72
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/post-edit-validate.js\"",
|
|
73
73
|
"timeout": 15000
|
|
74
74
|
}
|
|
75
75
|
]
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"hooks": [
|
|
80
80
|
{
|
|
81
81
|
"type": "command",
|
|
82
|
-
"command": "node \"
|
|
82
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/remind-php-review.js\"",
|
|
83
83
|
"timeout": 3000
|
|
84
84
|
}
|
|
85
85
|
]
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"hooks": [
|
|
90
90
|
{
|
|
91
91
|
"type": "command",
|
|
92
|
-
"command": "node \"
|
|
92
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/remind-sql-review.js\"",
|
|
93
93
|
"timeout": 3000
|
|
94
94
|
}
|
|
95
95
|
]
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"hooks": [
|
|
100
100
|
{
|
|
101
101
|
"type": "command",
|
|
102
|
-
"command": "node \"
|
|
102
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/scope-counter.js\"",
|
|
103
103
|
"timeout": 3000
|
|
104
104
|
}
|
|
105
105
|
]
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"hooks": [
|
|
111
111
|
{
|
|
112
112
|
"type": "command",
|
|
113
|
-
"command": "node \"
|
|
113
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/session-start.js\"",
|
|
114
114
|
"timeout": 5000
|
|
115
115
|
}
|
|
116
116
|
]
|
|
@@ -121,7 +121,7 @@
|
|
|
121
121
|
"hooks": [
|
|
122
122
|
{
|
|
123
123
|
"type": "command",
|
|
124
|
-
"command": "node \"
|
|
124
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/session-end.js\"",
|
|
125
125
|
"timeout": 5000
|
|
126
126
|
}
|
|
127
127
|
]
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
"hooks": [
|
|
131
131
|
{
|
|
132
132
|
"type": "command",
|
|
133
|
-
"command": "node \"
|
|
133
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/evaluate-session.js\"",
|
|
134
134
|
"timeout": 5000
|
|
135
135
|
}
|
|
136
136
|
]
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
"hooks": [
|
|
140
140
|
{
|
|
141
141
|
"type": "command",
|
|
142
|
-
"command": "node \"
|
|
142
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/remind-php-lint.js\"",
|
|
143
143
|
"timeout": 3000
|
|
144
144
|
}
|
|
145
145
|
]
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
| [Background Email-Template Worker (_Worker_Notification_EmailTemplate)](features/notification-email-template.md) | `_Worker_Notification_EmailTemplate::Send(...)` dispatches a **stored, client-defined `EmailTemplates` row off-thread** as a background WorkerJob. | worker2/Worker/Notification/EmailTemplate.php, worker2/Worker/Client/True.php, _underscore/Model/Client/EmailTemplate.php |
|
|
33
33
|
| [DB-Driven Notification (Internal) Email](features/notification-email.md) | Internal/notification emails (merge-conflict alerts, ops notices — anything system-generated, not client-facing transactional mail) are sent through one worker | worker2/Worker/Notification/Email.php, _underscore/Model/Client/EmailTemplate.php, dbchanges2/Client/2026-06-23a - EmailTemplateWrapper.sql, dbchanges2/Client_True/2026-06-23a - EmailTemplateWrapper.sql |
|
|
34
34
|
| [NYCHH Asset-Tag Backfill (worker2)](features/nychh-asset-tag-backfill.md) | Keeps NYC Health & Hospitals (`Client_Nychh`) unit asset tags and MAC addresses synced from NetSuite. | worker2/Worker/Client/Nychh.php, worker2/Worker/Client/Nychh/AssetTagBackfill.php, worker/crons/toga2/netsuite/verify_fulfillment_asset_tag_sync_nychh.php, worker/crons/toga2/netsuite/backfill_all_asset_tags_from_netsuite_nychh.php |
|
|
35
|
-
| [OneUptime Incident → ClickUp Task Sync (Monitor/Oneuptime/SyncIncidents)](features/oneuptime-incident-clickup-sync.md) | 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 eac | worker2/Component/Api/Oneuptime/Oneuptime.php, worker2/Worker/Monitor/Oneuptime.php, worker2/Config/production.ini, dbchanges2/Team/2026-08-10a - OneUptime Incident ClickUp Tasks.sql, dbchanges2/Core/2026-08-10b - OneUptime Incident Sync CronJob.sql |
|
|
35
|
+
| [OneUptime Incident → ClickUp Task Sync (Monitor/Oneuptime/SyncIncidents)](features/oneuptime-incident-clickup-sync.md) | 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 eac | worker2/Component/Api/Oneuptime/Oneuptime.php, worker2/Worker/Monitor/Oneuptime.php, worker2/Config/production.ini, dbchanges2/Team/2026-08-10a - OneUptime Incident ClickUp Tasks.sql, dbchanges2/Core/2026-08-10b - OneUptime Incident Sync CronJob.sql, dbchanges2/Team/2026-08-12a - OneUptime Incident Tasks Closed Marker.sql |
|
|
36
36
|
| [OneUptime push-metric monitors for 2.0 workers](features/oneuptime-worker2-monitoring.md) | A second, **OneUptime-reporting** monitoring pattern for the 2.0 worker2 tier, ported from the 1.0 `App_SystemMonitor_Compass` monitors. | worker2/Worker/Monitor/Compass.php, worker2/Worker/Client/Compass.php, worker2/composer.json, _underscore/Cloud.php, worker2/Worker/Monitor/Operations.php |
|
|
37
37
|
| [Platform Cache Cleanup (_Worker_Platform_Cache — Clean + Truncate)](features/platform-cache-cleanup.md) | `_Worker_Platform_Cache` owns maintenance of the shared **Cache** cluster that backs api2's [multi-client data retrieval](../../api2/features/cross-client-data- | worker2/Worker/Platform/Cache.php, worker2/Controller/Index.php, worker2/_.php, dbchanges2/Core/2026-07-27a - PlatformCacheCleanCron.sql |
|
|
38
38
|
| [Startech Webhook Handler (worker2)](features/startech-webhook-handler.md) | Receives inbound webhook events from Startech (Easeedesk) and creates or updates the corresponding ticket in TOGA 2.0. | worker2/Worker/Startech.php |
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-12
|
|
10
10
|
owners: ["jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Component/Api/Oneuptime/Oneuptime.php
|
|
@@ -14,6 +14,7 @@ files:
|
|
|
14
14
|
- worker2/Config/production.ini
|
|
15
15
|
- dbchanges2/Team/2026-08-10a - OneUptime Incident ClickUp Tasks.sql
|
|
16
16
|
- dbchanges2/Core/2026-08-10b - OneUptime Incident Sync CronJob.sql
|
|
17
|
+
- dbchanges2/Team/2026-08-12a - OneUptime Incident Tasks Closed Marker.sql
|
|
17
18
|
related:
|
|
18
19
|
- ./oneuptime-worker2-monitoring.md
|
|
19
20
|
- ./error-escalation-cron.md
|
|
@@ -41,7 +42,7 @@ reporter, smart monitor"; this feature adds the reader half.
|
|
|
41
42
|
open incidents (filters out resolved **in PHP**).
|
|
42
43
|
- `worker2/Worker/Monitor/Oneuptime.php` — `_Worker_Monitor_Oneuptime::SyncIncidents(): string`,
|
|
43
44
|
the cron action. Returns a structured summary string on every path
|
|
44
|
-
(`incidentsPolled` / `created` / `skipped` / `failed`).
|
|
45
|
+
(`incidentsPolled` / `created` / `skipped` / `failed`, plus `closed` / `closeFailed`).
|
|
45
46
|
|
|
46
47
|
## How it works
|
|
47
48
|
|
|
@@ -68,6 +69,36 @@ worker2 way to prevent duplicate ClickUp tasks under overlapping cron runs — p
|
|
|
68
69
|
from `_Worker_Infrastructure_Errors::createClickupTask`. `INSERT … WHERE NOT EXISTS` is
|
|
69
70
|
**insufficient** under REPEATABLE READ; the UNIQUE key is the concurrency arbiter.
|
|
70
71
|
|
|
72
|
+
### Close pass (auto-close on incident resolution)
|
|
73
|
+
|
|
74
|
+
The sync was originally **create-only**; a ClickUp task opened for an incident was never
|
|
75
|
+
closed once the incident resolved, so a recurrence (a *new* OneUptime incident with the same
|
|
76
|
+
monitor title) opened a second task that **looked like a duplicate** but was not (see the
|
|
77
|
+
gotcha below). `SyncIncidents()` now runs a **close pass first** each cycle, independent of how
|
|
78
|
+
many incidents are currently open, via `closeResolvedIncidents()`:
|
|
79
|
+
|
|
80
|
+
- SELECT ledger rows `WHERE clickupIdentifier IS NOT NULL AND dtClosed IS NULL`.
|
|
81
|
+
- For each, call `_Component_Api_Oneuptime::getIncidentResolvedState(string $incidentId): ?bool`
|
|
82
|
+
— `true` = resolved, `false` = confirmed still open, `null` = unknown. **Only a definite
|
|
83
|
+
`true` closes a task**; `null` means "leave alone" so an ambiguous read never closes a live
|
|
84
|
+
incident's ticket. This lookup uses a new per-incident route (`ROUTE__INCIDENT_ITEM`,
|
|
85
|
+
`/api/incident/{id}/get-item`, still a PLACEHOLDER) because resolved incidents no longer
|
|
86
|
+
appear in `getActiveIncidents()` and must be re-checked individually. A 2xx response missing
|
|
87
|
+
`currentIncidentState.isResolvedState` logs a warning (incident id only, no body/PII) so a
|
|
88
|
+
wrong placeholder shape is detectable rather than silently returning `null` forever.
|
|
89
|
+
- To close, PUT `/task/{id}` with the list's **closed-type status NAME** (ClickUp has no
|
|
90
|
+
generic close verb — see gotcha), resolved per-list via `GET /list/{id}` `statuses[]` where
|
|
91
|
+
`type == 'closed'`, cached per run in `$closedStatusByList`. Then stamp `dtClosed = NOW()`.
|
|
92
|
+
- `dtClosed` is stamped **only after a successful close**, so a failure retries next run.
|
|
93
|
+
Per-incident failures are caught (`Throwable`) + `error_log` + counted, not fatal; whole-run
|
|
94
|
+
failures (e.g. no sprint list) still re-throw for queue retry.
|
|
95
|
+
- All new ClickUp calls in the close path are throttled with
|
|
96
|
+
`usleep(_Worker_Clickup_Project::RATE_LIMIT_DELAY_US)` (120ms = 500/min), matching
|
|
97
|
+
`_Worker_Infrastructure_Errors`.
|
|
98
|
+
|
|
99
|
+
Helpers added: `closeResolvedIncidents()`, `closeClickupTask()`, `resolveClosedStatusForList()`,
|
|
100
|
+
`markClosed()`, and the `$closedStatusByList` per-run cache.
|
|
101
|
+
|
|
71
102
|
### Target list = current sprint list
|
|
72
103
|
|
|
73
104
|
The ClickUp task lands in the **current sprint** list, resolved by `GET /folder/{id}` and
|
|
@@ -80,8 +111,11 @@ id is read from `[clickup] sprint_folder_id` with a constant fallback (`90020432
|
|
|
80
111
|
|
|
81
112
|
`OneUptimeIncidentTasks` (CREATE on the **Team** cluster — chosen over Logs;
|
|
82
113
|
`dbchanges2/Team/2026-08-10a`): `id`, `uuid char(36)`, `dtCreated`,
|
|
83
|
-
`oneuptimeIncidentId varchar(64)`, `clickupIdentifier varchar(64) NULL
|
|
114
|
+
`oneuptimeIncidentId varchar(64)`, `clickupIdentifier varchar(64) NULL`, `dtClosed DATETIME NULL` (added
|
|
115
|
+
`dbchanges2/Team/2026-08-12a`, `AFTER clickupIdentifier`). The **UNIQUE KEY on
|
|
84
116
|
`oneuptimeIncidentId`** is the dedup contract, same rationale as `IssueClickupTasks`.
|
|
117
|
+
`dtClosed` marks a ledger row whose ClickUp task has been closed following incident
|
|
118
|
+
resolution (NULL = still open / not yet closed).
|
|
85
119
|
|
|
86
120
|
## Provisioning / deploy
|
|
87
121
|
|
|
@@ -104,13 +138,30 @@ id is read from `[clickup] sprint_folder_id` with a constant fallback (`90020432
|
|
|
104
138
|
|
|
105
139
|
## Gotchas / known issues
|
|
106
140
|
|
|
107
|
-
- **
|
|
108
|
-
|
|
109
|
-
|
|
141
|
+
- **Same-title ClickUp tickets from OneUptime are expected on incident recurrence.** Dedup is
|
|
142
|
+
**per-incident-id, not per-title**: the `oneuptimeIncidentId` UNIQUE key + claim-first insert
|
|
143
|
+
only prevents duplicates for the *same* incident id. Two tasks with the same monitor title
|
|
144
|
+
minutes apart are two *different* OneUptime incidents, not a dedup failure. The symptom that
|
|
145
|
+
looked like duplication was actually the missing auto-close: incident #1 resolved but its
|
|
146
|
+
task never closed, then a fresh incident opened a second task. Auto-close (above) closes
|
|
147
|
+
#1 on resolution so a recurrence (#2) is the only open ticket.
|
|
148
|
+
- **ClickUp has no generic "close" verb.** A task is closed by PUTting the exact NAME of the
|
|
149
|
+
status whose `type == 'closed'`, which is **list-specific** — resolved per-list from
|
|
150
|
+
`GET /list/{id}` `statuses[]` and cached per run.
|
|
151
|
+
- Throttle every ClickUp call in the close path with `usleep(RATE_LIMIT_DELAY_US)` (120ms) or
|
|
152
|
+
rate-limit failures degrade into `closeFailed` counts.
|
|
153
|
+
- **Both OneUptime routes remain PLACEHOLDERS** — the incident-list route
|
|
154
|
+
(`/api/incident/get-list`) *and* the new per-incident route (`/api/incident/{id}/get-item`,
|
|
155
|
+
`ROUTE__INCIDENT_ITEM`) are unverified against the live OneUptime API. Auto-close **fails
|
|
156
|
+
safe** (only a definite resolved `true` closes anything; `null` leaves the task alone) but
|
|
157
|
+
won't fire until the route/field shape is confirmed. Do not enable the cron (`isActive = 1`)
|
|
158
|
+
until the routes and returned fields are verified.
|
|
110
159
|
- **OneUptime cannot filter open-only server-side here** — resolution filtering happens in
|
|
111
160
|
PHP, so the returned field set must actually carry the resolved/open state.
|
|
112
161
|
- Ship the worker2 code deploy and both dbchanges2 migrations **together**; the cron row is
|
|
113
162
|
inert until the action is deployed and the ledger table exists.
|
|
163
|
+
- **Deploy order for the auto-close change:** run `dbchanges2/Team/2026-08-12a` (the `dtClosed`
|
|
164
|
+
column) **before** the worker2 code deploys — the new close-pass queries reference `dtClosed`.
|
|
114
165
|
|
|
115
166
|
## Change history
|
|
116
167
|
- 2026-08-10 — Built: first OneUptime API *reader* (`_Component_Api_Oneuptime`) + the
|
|
@@ -118,5 +169,13 @@ id is read from `[clickup] sprint_folder_id` with a constant fallback (`90020432
|
|
|
118
169
|
ClickUp task in the current sprint list; new `OneUptimeIncidentTasks` ledger (Team cluster,
|
|
119
170
|
UNIQUE on incident id) and a seeded-inactive `*/15 * * * *` cron row. Incident-list route
|
|
120
171
|
left PLACEHOLDER pending confirmation. (jcardinal)
|
|
172
|
+
- 2026-08-12 — Built: auto-close of ClickUp tasks when the OneUptime incident resolves (the
|
|
173
|
+
sync was create-only). Added `dtClosed` marker to the ledger
|
|
174
|
+
(`dbchanges2/Team/2026-08-12a`), `_Component_Api_Oneuptime::getIncidentResolvedState()` +
|
|
175
|
+
PLACEHOLDER per-incident route, and a close-first pass in `SyncIncidents()` (resolves each
|
|
176
|
+
list's `type=='closed'` status name, PUTs it, stamps `dtClosed` only on success, throttled
|
|
177
|
+
120ms; return string now carries `closed`/`closeFailed`). Discovered: apparent duplicate
|
|
178
|
+
same-title tickets were two distinct incidents — dedup is per-incident-id, not per-title; the
|
|
179
|
+
real gap was the missing close path. (jcardinal)
|
|
121
180
|
</content>
|
|
122
181
|
</invoke>
|
package/package.json
CHANGED