toga-ai 1.0.695 → 1.0.696

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.
@@ -25,5 +25,5 @@
25
25
  | [V2 request retry (Prong A) — deadlock AND retriable unique-key race, route-scoped in-process replay](features/v2-deadlock-retry.md) | api2's front controller ("**Prong A**") can **detect a retriable concurrency failure and replay the whole request in-process**, so a transient collision self-re | api2/Controller/Index.php, api2/Component/Api/V2/V2.php, _underscore/Database.php, _underscore/Query.php, _underscore/Model/Compass/AdvanceShippingNotice.php |
26
26
  | [V2 REST query contract (params, where grammar, encoding, ACL behavior)](features/v2-rest-query-contract.md) | What an **HTTP client** has to get right to query the Toga v2 REST API: which query params are recognized, the exact `where` grammar, how the query string is (n | api2/Component/Api/V2/V2.php |
27
27
  | [V2 reverse hasMany collections must be named in the fetch fields whitelist](features/v2-reverse-hasmany-fields-whitelist.md) | In the V2 JSON engine, a **reverse hasMany** relationship — the collection of child records that foreign-key back to a parent (e.g. | api2/Component/Api/V2/V2.php |
28
- | [AWS CodePipeline Deployment via CodeConnections (GitHub → Elastic Beanstalk)](workflows/codepipeline-codeconnections-deploy.md) | 2.0 apps (`api2`, `_underscore`) are deployed through **AWS CodePipeline**. | api2/.platform/hooks/postdeploy/060_register_instance_to_shared_application_load_balancer.sh, api2/ebs/register_instance_to_shared_application_load_balancer.php, api2/.platform/hooks/prebuild/git.sh, api2/.ebextensions/git.php, api2/.ebextensions/git.sandbox-dev.json |
28
+ | [AWS CodePipeline Deployment via CodeConnections (GitHub → Elastic Beanstalk)](workflows/codepipeline-codeconnections-deploy.md) | 2.0 apps (`api2`, `_underscore`) are deployed through **AWS CodePipeline**. | api2/.platform/hooks/postdeploy/060_register_instance_to_shared_application_load_balancer.sh, api2/ebs/register_instance_to_shared_application_load_balancer.php, api2/.platform/hooks/prebuild/git.sh, api2/.platform/hooks/postdeploy/015_install_composer.sh, api2/.ebextensions/git.php, api2/.ebextensions/git.sandbox-dev.json |
29
29
  | [New Environment Configuration & Provisioning (api2)](workflows/environment-configuration-and-provisioning.md) | What it takes for a 2.0 API environment (e.g. | api2/Config/<environment>.ini, api2/composer.json, api2/composer.lock, api2/Controller/Index.php, dbchanges2/Core/2026-06-16a - DatabaseHosts for new QA QC stage demo environments.sql, dbchanges2/Logs/, _underscore/Route.php |
@@ -197,6 +197,12 @@ always exits 0). See
197
197
  [the worker2 reference implementation](../worker2/features/alb-target-group-auto-registration.md) —
198
198
  api2's copy is the **unhardened original** and must be brought up to it.
199
199
 
200
+ **Deploy policy (2026-08-28):** the three production envs (`api-production-1/2/3`, 2 instances each)
201
+ run **Rolling / `BatchSize` 1** (was `AllAtOnce`/100%), target-group `HealthyThresholdCount` 2 — so
202
+ a deploy keeps ≥1 healthy backend and no longer emits the ELB-502 blip. Each CodePipeline
203
+ auto-deploys on a push to `_production`. See
204
+ [the CodePipeline deploy workflow](workflows/codepipeline-codeconnections-deploy.md).
205
+
200
206
  ## CI — commit message policy
201
207
 
202
208
  `.github/workflows/true-devteam-requirements.yml` enforces **`TRUE-{ticket}: {Subject}`** —
@@ -275,6 +281,27 @@ probe was starved of a PHP-FPM worker — do **not** chase client-traffic 5xx. *
275
281
  liveness from PHP-FPM worker availability so a slow-request burst can no longer flap it — see
276
282
  [health-check endpoint](features/health-check-endpoint.md).
277
283
 
284
+ **A fourth cause (deploy-triggered) — a burst of ELB `502`s when both instances restart at once.**
285
+ Confirmed on `api-production-1` (2026-08-28 23:51 UTC deploy: **43 × 502 in one minute**, the only
286
+ ELB 5xx in a 7-hour window). Under the old `AllAtOnce`/100% deploy policy, every deploy to a
287
+ 2-instance env restarts Apache/PHP-FPM on **both** instances simultaneously, so in-flight requests
288
+ have their connections dropped mid-response → a burst of **ELB-generated HTTP 502**. EB flags "X%
289
+ of requests to the ELB are failing with HTTP 5xx" `Degraded` ~3–4 min later and recovers in ~5 min.
290
+ Distinguishing fingerprint vs. a real app failure: it is **502** (a dropped backend connection),
291
+ **not 503** (no healthy backend) or **504** (backend timeout); **`HTTPCode_Target_5XX_Count` (app
292
+ 5xx) = 0**; **`HealthyHostCount` never drops below 2**; and there is **NOTHING in `Logs.Api`** —
293
+ api2 logs a request only *after* the response, and a 502 never completes one, so the request leaves
294
+ no log row. Diagnosis path: CloudWatch `HTTPCode_ELB_502/503/504_Count` vs
295
+ `HTTPCode_Target_5XX_Count`, plus EB `describe-events` (a version change) and CodePipeline
296
+ `list-pipeline-executions` — because a push to `_production` auto-deploys all three prod tiers, "we
297
+ didn't deploy" can be false (see
298
+ [the CodePipeline deploy workflow](workflows/codepipeline-codeconnections-deploy.md)).
299
+ **Rule:** an EB "% 5xx" `Degraded` with an **empty `Logs.Api` and 0 target-5xx** right after a
300
+ deploy is a deploy-restart 502 artifact, not an application bug — check the ELB-5xx breakdown and
301
+ the deploy/version timeline before hunting app bugs. **Fixed 2026-08-28:** all three prod envs moved
302
+ to **Rolling / batch-size 1** (≥1 healthy backend throughout a deploy), which eliminates the blip —
303
+ config + tradeoffs in the [deploy workflow](workflows/codepipeline-codeconnections-deploy.md).
304
+
278
305
  ## Known issues / accepted risks
279
306
 
280
307
  Open items a maintainer should know before changing this tier. None are "bugs to fix right now" —
@@ -352,6 +379,15 @@ they are the known sharp edges. Do not re-discover these from scratch.
352
379
  single-caller branches in `V2.php` as unverified until exercised directly.
353
380
 
354
381
  ## Change history
382
+ - 2026-08-28 — Recorded a **fourth EB-`Degraded` cause — the deploy-restart ELB 502**: under the old
383
+ `AllAtOnce`/100% policy a deploy restarts both instances of a 2-instance env at once, dropping
384
+ in-flight connections → an ELB-502 burst (43 in one minute on `api-production-1`, 2026-08-28) that
385
+ EB flags as "X% 5xx" Degraded ~3–4 min later. Fingerprint vs. an app bug: **502** (not 503/504),
386
+ **`HTTPCode_Target_5XX_Count`=0**, `HealthyHostCount` never < 2, and **nothing in `Logs.Api`** (a
387
+ 502 never completes a PHP response, which api2 logs only after). Diagnose via the ELB-5xx breakdown
388
+ + `describe-events`/`list-pipeline-executions` — a push to `_production` auto-deploys all three prod
389
+ tiers, so "we didn't deploy" can be false. Fixed by moving to Rolling/batch-1 (deploy config +
390
+ policy tradeoffs captured in the CodePipeline deploy workflow). (jcardinal)
355
391
  - 2026-08-28 — Recorded the third EB-`Degraded` cause: a no-stated-cause "reduced health" flip on `api-production-1` is the **PHP-served `/health` probe starved of a PHP-FPM worker** by off-peak bursts of slow failing Compass ASN POSTs (17–63 s, ~100% 500) — invisible in ALB `RequestCount`/`5xx` and never dropping `HealthyHostCount` (flap < 75 s deregistration). Correlated a 06:00–06:08 CST ASN burst to the 11:05 UTC Severe flip. Noted the fix (static-file `/health` decouples liveness from PHP-FPM) and updated the "Don't break `/health`" rule to the static-file form. (jcardinal)
356
392
  - 2026-08-26 — Documented and fixed a `/v2/auth/public` OOM: a present-but-null client `transactionId` (`{"transactionId": null}`) flowed into the request-logger's `_Model::load()` dup-check; because `Logs.Api.transactionId` is `FIELD_CHAR`, a null value collapses the WHERE and full-table-scans prod `Logs.Api` → memory exhaustion (the `_underscore` collapsed-WHERE mechanism). V2 only auto-uuids an *absent* transactionId (~L543), so a present null fell through (~L553 read → null). Fix (commit `36ef31f`): coerce null/empty transactionId → uuid in the auth block, and skip `load()` in the final logger dup-check (~L2277) when null/empty. Added the "don't blame the latest deploy — check first-occurrence" triage note. (jcardinal)
357
393
  - 2026-08-25 — **Resolved the auto-generated `Api.transactionId` 1062 collision** (was the 2026-07-28
@@ -6,12 +6,13 @@ project: API
6
6
  client: shared
7
7
  type: workflow
8
8
  status: active
9
- updated: 2026-08-25
9
+ updated: 2026-08-28
10
10
  owners: ["jcardinal", "mhammontree", "bala", "apeterson"]
11
11
  files:
12
12
  - api2/.platform/hooks/postdeploy/060_register_instance_to_shared_application_load_balancer.sh
13
13
  - api2/ebs/register_instance_to_shared_application_load_balancer.php
14
14
  - api2/.platform/hooks/prebuild/git.sh
15
+ - api2/.platform/hooks/postdeploy/015_install_composer.sh
15
16
  - api2/.ebextensions/git.php
16
17
  - api2/.ebextensions/git.sandbox-dev.json
17
18
  related:
@@ -62,8 +63,11 @@ are actually changing before you merge anything:
62
63
  `_<environment>`). Consequence: **a framework-only change requires REDEPLOYING THE CONSUMING
63
64
  TIER even when `api2` itself has zero changes** — an instance **restart will not pick it up**,
64
65
  because the clone happens in prebuild.
65
- - **`api2` has no deploy workflow.** Its only GitHub Action is the `TRUE-#####:` commit-message
66
- linter, so **EB deploys are manual**. Do not wait for CI to ship a framework change.
66
+ - **`api2` has no GitHub *Actions* deploy workflow** — its only GitHub Action is the
67
+ `TRUE-#####:` commit-message linter. **The production tiers, however, auto-deploy via AWS
68
+ CodePipeline on every push to `_production`** (a different system from GitHub Actions — see
69
+ *Production deploy: auto-trigger + rolling policy* below). Non-pipeline-triggered tiers are
70
+ still deployed by hand; don't wait for a GitHub Action to ship a framework change.
67
71
 
68
72
  ### ⚠ The `_underscore` SOURCE ACTION has its own branch trigger — and a green pipeline hides a wrong one
69
73
 
@@ -105,6 +109,64 @@ beta env actually received and returned when Sentry isn't wired up. (The Rate AI
105
109
  scans this same `Logs_Rate.Api` for failed outbound calls — see
106
110
  `clients/rate/features/aig-contract-creation.md`.)
107
111
 
112
+ ## Production deploy: auto-trigger + rolling policy (2026-08-28)
113
+
114
+ ### Production auto-deploys on every push to `_production` — a merge IS a deployment
115
+ The `api-production-1` (us-east-1), `api-production-2` (us-west-2), and `api-production-3`
116
+ (eu-west-1) CodePipelines each **auto-deploy on every push to branch `_production`** — a
117
+ **WebhookV2** trigger on the CodeConnections GitHub connection, watching source repos
118
+ `agilantsolutions/_underscore` and `agilantsolutions/api2`. There is **no separate manual deploy
119
+ step for production**: pushing or merging to `_production` **is** a production deployment to all
120
+ three regions. (This is why deploying a fix can itself page a moment later — see the
121
+ deploy-restart 502 below.)
122
+
123
+ **Triage consequence:** "we didn't deploy" can be false — any push to `_production` is a deploy.
124
+ Correlate an incident against the actual pipeline runs, not just remembered manual actions:
125
+ ```bash
126
+ aws codepipeline list-pipeline-executions --pipeline-name api-production-1 --region us-east-1
127
+ aws elasticbeanstalk describe-events --environment-name api-production-1 --region us-east-1 # version-change events
128
+ ```
129
+
130
+ ### ⚠ An EB `Degraded` right after a deploy = the deploy-restart 502
131
+ Under the old `AllAtOnce`/100% policy every deploy restarted Apache/PHP-FPM on **both**
132
+ instances of a 2-instance env simultaneously, dropping in-flight connections → a burst of
133
+ ELB-generated **HTTP 502** that EB flags as an "X% 5xx" `Degraded` ~3–4 min later (recovers in
134
+ ~5 min). This is a **deploy artifact, not an application failure** — the full diagnostic
135
+ fingerprint (502 not 503/504, app `HTTPCode_Target_5XX_Count`=0, `HealthyHostCount` never < 2,
136
+ **nothing in `Logs.Api`** because a 502 never completes a PHP response) lives in
137
+ [api2 architecture — EB "Degraded" health](../architecture.md). The rolling policy below
138
+ eliminates it.
139
+
140
+ ### Deploy policy: Rolling, batch size 1 (changed 2026-08-28)
141
+ All three production envs (app **"API"**, 2 instances each) were switched from
142
+ `AllAtOnce`/`Percentage`/`100` to **`DeploymentPolicy=Rolling`, `BatchSizeType=Fixed`,
143
+ `BatchSize=1`**, and the target group **`HealthyThresholdCount` dropped 3 → 2** (health path
144
+ `/health`, `HealthCheckInterval` 15 s, `UnhealthyThresholdCount` 5 unchanged). Rolling batch-1 on
145
+ a 2-instance env deploys in **2 batches with ≥1 healthy backend throughout**, so a deploy no
146
+ longer drops in-flight requests → **no more deploy-restart 502 blip**; the 3→2 threshold shaves
147
+ one 15 s interval (~45 s → 30 s) of per-batch health settle.
148
+
149
+ - Applied via a **config-only `update-environment`** (AWS CLI, per env) — no instance
150
+ replacement, no downtime; **takes effect on the NEXT deploy**.
151
+ - Namespaces: `aws:elasticbeanstalk:command` (`DeploymentPolicy` / `BatchSizeType` /
152
+ `BatchSize`); `aws:elasticbeanstalk:environment:process:default` (`HealthyThresholdCount`).
153
+ - Trade-off: Rolling is slower end-to-end than the old ~31 s `AllAtOnce`, and capacity briefly
154
+ drops to 1/2 instances per batch (fine at current traffic).
155
+
156
+ ### Deploy-policy guidance (don't reach for immutable expecting speed)
157
+ - **Immutable does NOT reduce deploy time.** It is the **slowest** policy — it boots a fresh
158
+ instance batch, health-checks it, then swaps, and temporarily **doubles** the instance count.
159
+ It does fully eliminate deploy 5xx and gives instant rollback, but it is not "faster."
160
+ - **`AllAtOnce` is fast (~31 s) precisely BECAUSE both instances restart at once** — which is
161
+ what causes the 502s. You cannot have "as fast as AllAtOnce" **and** "zero 502s."
162
+ - **Rolling batch-1 is the sweet spot** for a 2-instance env.
163
+ - **`RollingWithAdditionalBatch`** is the option only if zero capacity loss during a deploy is
164
+ ever required (it adds a temporary instance; slower and costlier).
165
+ - The real deploy-**time** levers are independent of the policy: the health-check settle
166
+ (`HealthCheckInterval` × `HealthyThresholdCount`) and per-instance postdeploy work — api2 runs
167
+ `composer install` on **every** deploy via
168
+ `.platform/hooks/postdeploy/015_install_composer.sh` (a caching / pre-bake candidate).
169
+
108
170
  ## Steps
109
171
  1. A push to the watched branch (e.g. `_stage`) triggers the pipeline.
110
172
  2. The Source stage uses `CodeStarSourceConnection` actions, each referencing:
@@ -233,6 +295,20 @@ aws codeconnections get-connection --connection-arn "<CONN_ARN>" --region "$REGI
233
295
  ```
234
296
 
235
297
  ## Change history
298
+ - 2026-08-28 — Recorded the **production deploy trigger + policy change**: the `api-production-1/2/3`
299
+ CodePipelines **auto-deploy on every push to `_production`** (WebhookV2 on the CodeConnections
300
+ connection, repos `_underscore` + `api2`), so a merge to `_production` **is** a three-region prod
301
+ deploy — correlate incidents against `list-pipeline-executions`, not remembered manual actions.
302
+ Switched all three 2-instance envs from `AllAtOnce`/100% to **Rolling / Fixed / BatchSize 1** and
303
+ dropped the target-group **`HealthyThresholdCount` 3 → 2** (config-only `update-environment`,
304
+ effective next deploy) to eliminate the **deploy-restart 502 blip** (both instances restarting at
305
+ once dropped in-flight requests → ELB 502 → EB "X% 5xx" Degraded). Added deploy-policy guidance:
306
+ immutable is the **slowest** policy (not faster) and doubles instances; `AllAtOnce` is fast only
307
+ because it restarts both at once (the 502 cause); Rolling batch-1 is the sweet spot;
308
+ `RollingWithAdditionalBatch` only if zero capacity loss is needed; the real deploy-time levers are
309
+ the health-check settle and the per-deploy `015_install_composer.sh` `composer install`. Corrected
310
+ the "EB deploys are manual" note — that is only true of the absent GitHub *Actions* workflow; prod
311
+ deploys via CodePipeline are automatic. (jcardinal)
236
312
  - 2026-08-25 — Documented that the Source stage's **`_underscore` action has its own AWS-side branch
237
313
  trigger**, independent of `git.<env>.json` and invisible in the repo — a second way to silently run
238
314
  the wrong framework branch. `ApiSandboxClient` ran `_production` from a mis-set source action while
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.695",
3
+ "version": "1.0.696",
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",