toga-ai 1.0.619 → 1.0.620

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.
@@ -6,7 +6,7 @@ project: Worker
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-08-17
9
+ updated: 2026-08-19
10
10
  owners: [jcardinal]
11
11
  files:
12
12
  - worker2/Worker/Infrastructure/CloudWatch.php
@@ -28,7 +28,10 @@ OneUptime string-matches — OneUptime cannot compare numbers on a pushed body.
28
28
 
29
29
  The load-bearing design decision this doc records is the **paging (alarm) criteria**: page
30
30
  on EB `Degraded`/`Severe`, but **suppress the common "an occasional HTTP 500 tripped
31
- Degraded" false alarm** unless 5xx errors actually dominate the request mix.
31
+ Degraded/Severe" false alarm** unless 5xx errors actually dominate the request mix (by
32
+ share) or reach a real absolute volume. The 5xx gate applies to **both** `Degraded` and
33
+ `Severe`, and only when EB attributes the problem **solely** to 5xx — a mixed cause always
34
+ pages.
32
35
 
33
36
  **Critical behavior:** the method is **non-fatal and never throws** — it always returns a
34
37
  string. worker2 has **no DLQ and a 3600s SQS visibility timeout**, so any uncaught 500
@@ -53,15 +56,33 @@ itself runs non-fatal.
53
56
  Per environment, `alarm=HIGH` (page) when the EB `HealthStatus` is `Degraded` **or**
54
57
  `Severe`. `Suspended` never pages.
55
58
 
56
- - **`Severe` always pages.**
57
- - **`Degraded` for a non-5xx reason** (latency, instances down, …) **always pages.**
58
- - **`Degraded` attributed to HTTP 5xx errors pages only when 5xx errors dominate** — i.e.
59
- the 5xx share of requests is at/above `HTTP_5XX_ALARM_RATIO` (default 0.80). An occasional
60
- 500 that trips Degraded is noise and must **not** page; a flood is a real problem and
61
- **must** page.
62
- - **Tiny-sample guard:** below `HTTP_5XX_MIN_REQUEST_COUNT` (default 20) requests in the
63
- window, do **not** trust the ratio — page rather than let a 2-of-3 blip read as "80%
64
- failing."
59
+ The **5xx gate applies to both `Degraded` and `Severe`.** A non-5xx or mixed-cause
60
+ `Degraded`/`Severe` — and any env with no stated cause — **always pages**; the gate only
61
+ ever suppresses an env EB attributes **solely** to 5xx.
62
+
63
+ - **Non-5xx or mixed-cause `Degraded`/`Severe`** (latency, instances down, a failed deploy,
64
+ or a 5xx trickle *alongside* a real non-5xx failure) **always pages.**
65
+ - **Solely-5xx `Degraded`/`Severe` pages only on a genuine flood** — either:
66
+ - the **absolute** 5xx count in the window is at/above `HTTP_5XX_ALARM_ABSOLUTE_COUNT`
67
+ (default 20), **or**
68
+ - the **5xx share** of requests is at/above `HTTP_5XX_ALARM_RATIO` (default 0.80).
69
+ - **Zero requests in the window → no page** (nothing observed; also avoids divide-by-zero).
70
+
71
+ **Why the gate now covers `Severe` too:** EB escalates an environment to `Severe` precisely
72
+ when the 5xx share is high, so real 5xx floods reach `Severe` *first* and never touch the
73
+ gated `Degraded` branch. When the gate applied only to `Degraded`, the 80% threshold was
74
+ effectively dead — a real 50%-5xx `Severe` event paged despite the setting. Gating both
75
+ tiers makes the threshold actually govern.
76
+
77
+ **Why "solely" 5xx, not "any" 5xx cause:** a mixed-cause env (a 5xx trickle plus a real
78
+ non-5xx failure such as a failed deploy) must not be silenced by a sub-threshold ratio. The
79
+ ratio gate only applies when **every** stated cause references 5xx (and there is at least
80
+ one); anything else pages.
81
+
82
+ **Why an absolute floor in addition to the ratio:** the ratio alone is magnitude-blind —
83
+ 60k failing out of 100k reads as 60% and would be suppressed, though it is plainly a flood.
84
+ `HTTP_5XX_ALARM_ABSOLUTE_COUNT` pages such an env regardless of share; tune it to the busiest
85
+ monitored environment's request volume.
65
86
 
66
87
  ### Fail-safe: a blind read pages, never reads healthy
67
88
 
@@ -76,18 +97,23 @@ signal — same alarm-vs-probe split as the multi-client monitors in
76
97
  | Constant | Default | Meaning |
77
98
  |---|---|---|
78
99
  | `HEALTH_ALARM_STATUSES` | `['Degraded','Severe']` | HealthStatus values that page |
79
- | `HTTP_5XX_ALARM_RATIO` | `0.80` | 5xx share (0.0–1.0) at/above which a 5xx-driven `Degraded` pages |
80
- | `HTTP_5XX_MIN_REQUEST_COUNT` | `20` | Below this many requests in the window, page rather than trust the ratio |
100
+ | `HTTP_5XX_ALARM_RATIO` | `0.80` | 5xx share (0.0–1.0) at/above which a solely-5xx `Degraded`/`Severe` pages |
101
+ | `HTTP_5XX_ALARM_ABSOLUTE_COUNT` | `20` | Absolute 5xx count in the window at/above which a solely-5xx `Degraded`/`Severe` pages regardless of share |
81
102
  | `HTTP_5XX_CAUSE_MARKERS` | `['5xx','http 5']` | Case-insensitive substrings identifying a 5xx-attributed EB `Cause` |
82
103
 
83
104
  ### Implementation shape
84
105
 
85
106
  - `shouldPageForHealth(healthStatus, causes, applicationMetrics): [bool, ?float]` — returns
86
107
  the page decision and the observed 5xx ratio.
87
- - `causesAttributeTo5xx(causes): bool` — substring-matches `HTTP_5XX_CAUSE_MARKERS` against
88
- the EB `Causes` strings (case-insensitive).
108
+ - `causesAttributeSolelyTo5xx(causes): bool` — true only when **every** EB `Cause` references
109
+ 5xx **and there is at least one** cause. Replaces the former `causesAttributeTo5xx()`, which
110
+ returned true if *any* cause mentioned 5xx and so let a mixed-cause env be gated.
111
+ - `causeIsFivexx(cause): bool` — helper that substring-matches `HTTP_5XX_CAUSE_MARKERS`
112
+ against a single EB `Cause` string (case-insensitive); `causesAttributeSolelyTo5xx` requires
113
+ it to hold for all causes.
89
114
  - The ratio is computed as `StatusCodes.Status5xx / RequestCount` from `ApplicationMetrics`
90
- (raw counts — see below), **not** by parsing the percentage out of the `Causes` text.
115
+ (raw counts — see below), **not** by parsing the percentage out of the `Causes` text. The
116
+ absolute-count floor reads `StatusCodes.Status5xx` directly.
91
117
  - `describeEnvironmentHealth` is called with
92
118
  `AttributeNames = ['HealthStatus','Status','Color','Causes','ApplicationMetrics']`.
93
119
  - Per-environment report + OneUptime payload now include the per-env `alarm` token and the
@@ -147,9 +173,10 @@ iteration:** a deploy that fails fast *without ever degrading health* is not cau
147
173
  **whole run** via the outer backstop instead of paging just that region's envs as blind.
148
174
  Candidate follow-up now that blind reads page.
149
175
  - **No first-party test harness exists in worker2** (all tests are vendor/).
150
- `shouldPageForHealth()` and `causesAttributeTo5xx()` are pure and ideal to unit-test
151
- (ratio at exactly 0.80; count 19 vs 20; `Severe` over a 5xx cause; empty causes; null
152
- metrics) — deferred pending a harness.
176
+ `shouldPageForHealth()` and `causesAttributeSolelyTo5xx()` are pure and ideal to unit-test
177
+ (ratio at exactly 0.80; absolute count 19 vs 20; `Severe` over a solely-5xx cause; a
178
+ mixed 5xx + non-5xx cause; empty causes; null metrics; zero requests) — deferred pending a
179
+ harness.
153
180
 
154
181
  ## Gotchas / known issues
155
182
 
@@ -164,8 +191,28 @@ iteration:** a deploy that fails fast *without ever degrading health* is not cau
164
191
  — never parse the percentage out of `Causes`.**
165
192
  - **`oneuptimeUrl` is a push credential** — it arrives as a cron parameter; never log it or
166
193
  record its value in a doc.
194
+ - **Near-idle ratio edge (residual, accepted).** With the tiny-sample guard removed, a
195
+ near-idle env whose window holds a tiny all-error sample (e.g. its only request being a
196
+ 500 = 100%) still trips the ratio and pages. Optional future guard: require a minimum 5xx
197
+ count before the ratio applies (the absolute floor gates high volume, not this low-volume
198
+ edge).
167
199
 
168
200
  ## Change history
201
+ - 2026-08-19 — Overhauled the 5xx alarm gating in `shouldPageForHealth()`. The 80% share
202
+ gate now applies to **both** `Degraded` and `Severe` (was `Degraded`-only, which left the
203
+ threshold dead because EB escalates real 5xx floods straight to `Severe` — a 50%-5xx
204
+ `Severe` had been paging despite the 0.80 setting). This Severe-gating trade-off was made
205
+ deliberately with an independent architecture second opinion on record (cto returned
206
+ DISAGREE-WITH-ALTERNATIVE; developer accepted). Narrowed 5xx classification: replaced
207
+ `causesAttributeTo5xx()` (any cause mentions 5xx) with `causesAttributeSolelyTo5xx()` (every
208
+ cause references 5xx, ≥1) + a `causeIsFivexx()` helper, so a mixed cause (5xx trickle beside
209
+ a real non-5xx failure) always pages — a security-review gap the Severe change would have
210
+ widened. Added `HTTP_5XX_ALARM_ABSOLUTE_COUNT` (20): a solely-5xx env pages regardless of
211
+ share once the absolute 5xx count reaches the floor (closes the ratio's magnitude-blindness,
212
+ e.g. 60k of 100k). Removed `HTTP_5XX_MIN_REQUEST_COUNT` (was 20) and its tiny-sample
213
+ page-anyway guard — a small number of 5xx is now judged purely on the ratio; below the
214
+ absolute floor, page only if share ≥ 0.80, and zero requests → no page. Accepted residual:
215
+ a near-idle env with a tiny all-error sample still trips the ratio. (jcardinal)
169
216
  - 2026-08-17 — Created. Documented the `ElasticBeanstalkHealth` alarm/paging criteria (page
170
217
  on `Degraded`/`Severe`; suppress a 5xx-driven `Degraded` unless the 5xx share ≥
171
218
  `HTTP_5XX_ALARM_RATIO` 0.80 with an `HTTP_5XX_MIN_REQUEST_COUNT` 20 tiny-sample guard;
@@ -178,5 +225,3 @@ iteration:** a deploy that fails fast *without ever degrading health* is not cau
178
225
  covers the reads). Recorded the reverted `['Severe']`-only + deployment-failure-detector
179
226
  direction and the accepted residual gap (a deploy that fails without degrading health).
180
227
  (jcardinal)
181
- </content>
182
- </invoke>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.619",
3
+ "version": "1.0.620",
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",