Take a Longhorn snapshot and backup of gitea/gitea-data, and confirm the off-cluster backup (backup.policy: daily-offcluster) is recent. The 1.27 -> 28 DB migration (SQLite on that PVC) is one-way. I could not read the backup age from my worktree, so it is UNVERIFIED.
Merge in a quiet window. Gitea is the merge gate, the CI host and prod's Flux GitRepository source. The Deployment is strategy: Recreate, so while the new pod migrates and starts there is no Gitea at all: no CI runs, no source fetches, no git pushes. Do not queue other merges behind this one.
No /approve-override is needed. No sensitive path (infra/gitea/approval-policy.json) is touched; the PR changes catalog/, apps/wave-0/, renovate.json5 and decisions/ only.
POST-MERGE CHECK
GET https://gitea.home.keithjackson.dev/api/v1/version returns 28.3.0. Until then the apps-wave-0 Kustomization has not rolled.
Sign in through Keycloak SSO in a private window. The pod log should also show [gitea-oidc] login source 'keycloak' registered and verified from the postStart hook, and /data/gitea-oidc-bootstrap.log should have no new FAILED line.
A PR gate run (gate / gate (pull_request)) completes on the incluster-dind runner. Opening or re-pushing any PR will start one.
Optional: the startup log has no [security] ALLOWED_HOST_LIST / egress warnings.
Rollback
Restore gitea-data from the pre-upgrade snapshot or backup, with the matching 1.27.3 image. Do NOT simply revert the image tag. 28 migrates the DB schema forward, and 1.27.3 running against a migrated DB is not a supported state. Nothing reverts the image automatically: update.autoRollbackOnFailedHealth: true only ever renders an annotation on a Flux ImagePolicy, and apps/ is rendered --no-image-automation (ci/render-golden.sh), so no ImagePolicy exists. The container has no probes, and a Deployment never self-reverts. I kept the flag (R-UPD-03) and added a comment in the catalog saying it is inert for this service and why.
What this changes
File
Change
catalog/gitea.yaml
image gitea/gitea:1.27.3 -> gitea/gitea:28.3.0; update.range">=1.22.0 <2.0.0" -> ">=28.0.0 <29.0.0" (the schema takes free-form range strings); env changes below
apps/wave-0/gitea.yaml
re-rendered with the canonical render.py --all catalog -o apps --wave-layout --no-image-automation in the toolbox. Only gitea changed
renovate.json5
gitea allowedVersions<2.0.0 -> <29.0.0, so Renovate proposes 28.x patches and minors (never auto-merged: gitea is on the blast-radius list) and never 29. The description no longer calls 28.x a stale tag. Checked with renovate-config-validator at 44.167.0 (the tag renovate.yaml pins): Config validated successfully
decisions/2026-11-07-gitea-28.md
handoff entry
The runner (apps/wave-1/gitea-actions.yaml) and dind are not touched; they get separate PRs.
28.3.0 is the newest stable 28.x tag on both Docker Hub (28.0.0, 28.1.0, 28.2.0, 28.3.0) and GitHub (v28.0.0..v28.3.0), checked 2026-11-07.
Egress policy for outbound HTTP (https://github.com/go-gitea/gitea/pull/39426): [security] EGRESS_MODE (lax by default, or strict) and [security] ALLOWED_HOST_LIST. In lax mode, public targets are allowed and private, loopback or CGNAT targets need an allow entry. (source) For those policies, a hostname entry does not admit a private address. WithLocalNeedsIPAllow requires an IP or CIDR entry, or the built-in private / loopback (modules/egress/policy/policy.go).
Keycloak SSO is not affected. (source)egress.NewSecurityPolicy has exactly two callers: the OAuth2 avatar fetch (routers/web/auth/oauth.go; [oauth2_client] UPDATE_AVATAR is off here) and OpenID 2.0 (modules/auth/openid, unused). The OIDC login source is goth's openidConnect.New(...) with its default HTTP client (services/auth/source/oauth2/providers_openid.go), so it never reaches the egress check. keycloak.${BASE_DOMAIN} resolving to the private Gateway address in-cluster therefore does not matter to it.
Webhooks and migrations/mirrors are covered, but this instance has 0 repo, org or system webhooks and 0 mirrors (measured against the live API, 2026-11-07). 1.27 also already refused private webhook targets by default, so nothing that works today is newly denied.
Actions uses: fetching (DEFAULT_ACTIONS_URL=github) is done by the runner, not by Gitea, so it is unaffected.
Handled by: declaring GITEA__security__EGRESS_MODE=lax with no allow list, on purpose. A hostname entry would not help for a private IP, and a CIDR entry would only widen webhook SSRF reach for no current consumer. The catalog comment says to add the narrowest CIDR (Gateway /32) only if a real private webhook target appears. This differs from the brief, which expected an allow-list entry. The source shows the premise does not hold for 28.3.0.
[server] DOMAIN is ignored (https://github.com/go-gitea/gitea/pull/39400). The domain and the SSH domain come from ROOT_URL, which was already set. (source)modules/setting/server.go no longer reads it; the only remaining DOMAIN key is [session]. Grepping the repo found no other reader of GITEA__server__DOMAIN; bootstrap/gitea/bootstrap-gitea.sh sets only ROOT_URL and SSH_PORT. Handled by: removing the env line. The old value that environment-to-ini already wrote into app.ini stays there and is inert.
Actions run retention (https://github.com/go-gitea/gitea/pull/38855): completed runs, with their jobs, logs and artifacts, are deleted after 400 days by the cleanup_action_runs cron. (source)modules/setting/actions.go: defaultRunRetentionDays = 400. The spec repo has no CI-history retention requirement. Handled by: declaring GITEA__actions__RUN_RETENTION_DAYS=400 (upstream's value, now explicit).
Self-registration is disabled by default (#39400; 1.27 defaulted it open). (source) OAuth2 auto-registration (routers/web/auth/oauth.go) depends on ALLOW_ONLY_INTERNAL_REGISTRATION and ENABLE_AUTO_REGISTRATION, not on DISABLE_REGISTRATION, so Keycloak auto-provisioning keeps working. Handled by: declaring GITEA__service__DISABLE_REGISTRATION=true. Accounts now arrive only through Keycloak or the CLI.
Git >= 2.25.0 required (#39131). The official image is alpine 3.24 with distro git (Dockerfile at v28.3.0), so no action is needed.
postStart hook CLI. (source) The commands are unchanged at v28.3.0:
admin user list --admin prints ID Username Email IsActive IsAdmin 2FA Type, so awk '{print $2}' is still the username.
admin auth list prints ID Name Type Enabled, so $1/$2 are still right.
admin user create keeps --username --password --email --admin --must-change-password.
curl and su-exec are still in the image, and the s6 setup still runs environment-to-ini (so the GITEA__* env vars still apply).
Handled by: no change.
Actions workflow evaluation (#39358): job-level if: is evaluated before matrix expansion, fail-fast is enforced, and private reusable workflows are restricted. No workflow under .gitea/workflows/ uses matrix or strategy, so nothing changes.
Live notifications move to a WebSocket at /-/ws (#36965). If the Gateway does not forward the upgrade, notifications fall back to polling, so this is not a break. UNVERIFIED on the Envoy Gateway.
Startup migration under Flux: the container has no liveness probe, so a slow migration cannot be killed part-way by kubelet. apps-wave-0 has timeout: 5m, wait: true. A migration longer than that would mark the wave unhealthy for a while, but it would not interrupt the migration.
Local gate (toolbox, committed tree 8a9baa9)
DEV_KUBECONFIG=/dev/null ENGINE=<.git-mount shim> ./infra/devcontainer/dev.sh run bash -c './ci/gate.sh' -> rc 3 (UNDETERMINED), not green.
spec-immutability pass · decisions-log pass (76 entries; 75 on main + this one, which shows the gate saw the commit) · lint pass · l0 pass · render-golden pass · l1 pass (3/3) · approval-policy pass (N/A offline) · gitea-settings SKIPPED (no SETTINGS_API_TOKEN; the only stage that did not run) · conformance L2/L3 SKIPPED (no cluster, Phase 0).
Not verified
Nothing booted 28.3.0 against a copy of the production DB. Migration duration and success are UNVERIFIED.
## PRE-MERGE OPERATOR CHECKLIST
- [x] **Take a Longhorn snapshot and backup of `gitea/gitea-data`**, and confirm the off-cluster backup (`backup.policy: daily-offcluster`) is recent. The 1.27 -> 28 DB migration (SQLite on that PVC) is **one-way**. I could not read the backup age from my worktree, so it is UNVERIFIED.
- [ ] **Merge in a quiet window.** Gitea is the merge gate, the CI host and prod's Flux `GitRepository` source. The Deployment is `strategy: Recreate`, so while the new pod migrates and starts there is no Gitea at all: no CI runs, no source fetches, no git pushes. Do not queue other merges behind this one.
- [ ] No `/approve-override` is needed. No sensitive path (`infra/gitea/approval-policy.json`) is touched; the PR changes `catalog/`, `apps/wave-0/`, `renovate.json5` and `decisions/` only.
## POST-MERGE CHECK
- [ ] `GET https://gitea.home.keithjackson.dev/api/v1/version` returns `28.3.0`. Until then the apps-wave-0 Kustomization has not rolled.
- [ ] Sign in through **Keycloak SSO** in a private window. The pod log should also show `[gitea-oidc] login source 'keycloak' registered and verified` from the postStart hook, and `/data/gitea-oidc-bootstrap.log` should have no new `FAILED` line.
- [ ] A PR gate run (`gate / gate (pull_request)`) completes on the `incluster-dind` runner. Opening or re-pushing any PR will start one.
- [ ] Optional: the startup log has no `[security] ALLOWED_HOST_LIST` / egress warnings.
## Rollback
**Restore `gitea-data` from the pre-upgrade snapshot or backup, with the matching 1.27.3 image. Do NOT simply revert the image tag.** 28 migrates the DB schema forward, and 1.27.3 running against a migrated DB is not a supported state. Nothing reverts the image automatically:
`update.autoRollbackOnFailedHealth: true` only ever renders an annotation on a Flux `ImagePolicy`, and `apps/` is rendered `--no-image-automation` (`ci/render-golden.sh`), so no ImagePolicy exists. The container has no probes, and a Deployment never self-reverts. I kept the flag (R-UPD-03) and added a comment in the catalog saying it is inert for this service and why.
## What this changes
| File | Change |
|---|---|
| `catalog/gitea.yaml` | image `gitea/gitea:1.27.3` -> `gitea/gitea:28.3.0`; `update.range` `">=1.22.0 <2.0.0"` -> `">=28.0.0 <29.0.0"` (the schema takes free-form range strings); env changes below |
| `apps/wave-0/gitea.yaml` | re-rendered with the canonical `render.py --all catalog -o apps --wave-layout --no-image-automation` in the toolbox. Only gitea changed |
| `renovate.json5` | gitea `allowedVersions` `<2.0.0` -> `<29.0.0`, so Renovate proposes 28.x patches and minors (never auto-merged: gitea is on the blast-radius list) and never 29. The description no longer calls 28.x a stale tag. Checked with `renovate-config-validator` at **44.167.0** (the tag `renovate.yaml` pins): `Config validated successfully` |
| `decisions/2026-11-07-gitea-28.md` | handoff entry |
The runner (`apps/wave-1/gitea-actions.yaml`) and dind are not touched; they get separate PRs.
28.3.0 is the newest stable 28.x tag on both Docker Hub (`28.0.0`, `28.1.0`, `28.2.0`, `28.3.0`) and GitHub (`v28.0.0`..`v28.3.0`), checked 2026-11-07.
## Breaking changes and how each is handled
Sources: release notes https://blog.gitea.com/release-of-28.0.0/ , https://github.com/go-gitea/gitea/releases ; config reference https://docs.gitea.com/administration/config-cheat-sheet and `custom/conf/app.example.ini` at `v28.3.0`; upgrade guide https://docs.gitea.com/installation/upgrade-from-gitea . Claims marked **(source)** were checked against the v28.3.0 Go source.
1. **Egress policy for outbound HTTP** (https://github.com/go-gitea/gitea/pull/39426): `[security] EGRESS_MODE` (`lax` by default, or `strict`) and `[security] ALLOWED_HOST_LIST`. In lax mode, public targets are allowed and private, loopback or CGNAT targets need an allow entry. **(source)** For those policies, a *hostname* entry does not admit a private address. `WithLocalNeedsIPAllow` requires an IP or CIDR entry, or the built-in `private` / `loopback` (`modules/egress/policy/policy.go`).
- **Keycloak SSO is not affected. (source)** `egress.NewSecurityPolicy` has exactly two callers: the OAuth2 *avatar* fetch (`routers/web/auth/oauth.go`; `[oauth2_client] UPDATE_AVATAR` is off here) and OpenID 2.0 (`modules/auth/openid`, unused). The OIDC login source is goth's `openidConnect.New(...)` with its default HTTP client (`services/auth/source/oauth2/providers_openid.go`), so it never reaches the egress check. `keycloak.${BASE_DOMAIN}` resolving to the private Gateway address in-cluster therefore does not matter to it.
- **Webhooks and migrations/mirrors** are covered, but this instance has **0** repo, org or system webhooks and **0** mirrors (measured against the live API, 2026-11-07). 1.27 also already refused private webhook targets by default, so nothing that works today is newly denied.
- **Actions `uses:` fetching** (`DEFAULT_ACTIONS_URL=github`) is done by the runner, not by Gitea, so it is unaffected.
- **Handled by:** declaring `GITEA__security__EGRESS_MODE=lax` with **no allow list**, on purpose. A hostname entry would not help for a private IP, and a CIDR entry would only widen webhook SSRF reach for no current consumer. The catalog comment says to add the narrowest CIDR (Gateway `/32`) only if a real private webhook target appears. *This differs from the brief, which expected an allow-list entry. The source shows the premise does not hold for 28.3.0.*
2. **`[server] DOMAIN` is ignored** (https://github.com/go-gitea/gitea/pull/39400). The domain and the SSH domain come from `ROOT_URL`, which was already set. **(source)** `modules/setting/server.go` no longer reads it; the only remaining `DOMAIN` key is `[session]`. Grepping the repo found no other reader of `GITEA__server__DOMAIN`; `bootstrap/gitea/bootstrap-gitea.sh` sets only ROOT_URL and SSH_PORT. **Handled by:** removing the env line. The old value that `environment-to-ini` already wrote into `app.ini` stays there and is inert.
3. **Actions run retention** (https://github.com/go-gitea/gitea/pull/38855): completed runs, with their jobs, logs and artifacts, are deleted after 400 days by the `cleanup_action_runs` cron. **(source)** `modules/setting/actions.go`: `defaultRunRetentionDays = 400`. The spec repo has no CI-history retention requirement. **Handled by:** declaring `GITEA__actions__RUN_RETENTION_DAYS=400` (upstream's value, now explicit).
4. **Self-registration is disabled by default** (#39400; 1.27 defaulted it open). **(source)** OAuth2 auto-registration (`routers/web/auth/oauth.go`) depends on `ALLOW_ONLY_INTERNAL_REGISTRATION` and `ENABLE_AUTO_REGISTRATION`, not on `DISABLE_REGISTRATION`, so Keycloak auto-provisioning keeps working. **Handled by:** declaring `GITEA__service__DISABLE_REGISTRATION=true`. Accounts now arrive only through Keycloak or the CLI.
5. **Git >= 2.25.0 required** (#39131). The official image is alpine 3.24 with distro git (`Dockerfile` at v28.3.0), so no action is needed.
6. **postStart hook CLI. (source)** The commands are unchanged at v28.3.0:
- `admin user list --admin` prints `ID Username Email IsActive IsAdmin 2FA Type`, so `awk '{print $2}'` is still the username.
- `admin auth list` prints `ID Name Type Enabled`, so `$1`/`$2` are still right.
- `add-oauth` and `update-oauth` keep `--name --provider --key --secret --auto-discover-url --scopes --group-claim-name --admin-group --id`.
- `admin user create` keeps `--username --password --email --admin --must-change-password`.
- `curl` and `su-exec` are still in the image, and the s6 `setup` still runs `environment-to-ini` (so the `GITEA__*` env vars still apply).
- **Handled by:** no change.
7. **Actions workflow evaluation** (#39358): job-level `if:` is evaluated before matrix expansion, `fail-fast` is enforced, and private reusable workflows are restricted. No workflow under `.gitea/workflows/` uses `matrix` or `strategy`, so nothing changes.
8. **Live notifications move to a WebSocket at `/-/ws`** (#36965). If the Gateway does not forward the upgrade, notifications fall back to polling, so this is not a break. UNVERIFIED on the Envoy Gateway.
9. **Startup migration under Flux:** the container has no liveness probe, so a slow migration cannot be killed part-way by kubelet. apps-wave-0 has `timeout: 5m, wait: true`. A migration longer than that would mark the wave unhealthy for a while, but it would not interrupt the migration.
## Local gate (toolbox, committed tree `8a9baa9`)
`DEV_KUBECONFIG=/dev/null ENGINE=<.git-mount shim> ./infra/devcontainer/dev.sh run bash -c './ci/gate.sh'` -> **rc 3 (UNDETERMINED), not green.**
spec-immutability **pass** · decisions-log **pass** (76 entries; 75 on main + this one, which shows the gate saw the commit) · lint **pass** · l0 **pass** · render-golden **pass** · l1 **pass** (3/3) · approval-policy **pass** (N/A offline) · gitea-settings **SKIPPED** (no `SETTINGS_API_TOKEN`; the only stage that did not run) · conformance L2/L3 **SKIPPED** (no cluster, Phase 0).
## Not verified
- Nothing booted 28.3.0 against a copy of the production DB. Migration duration and success are UNVERIFIED.
- The age of the latest backup (see the checklist).
- L2/L3 conformance does not run in CI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Gitea dropped the "1." prefix; 28.x is the successor of 1.27.
- image 28.3.0, update.range >=28.0.0 <29.0.0, Renovate ceiling <29.0.0
- drop GITEA__server__DOMAIN (no longer read in 28, ROOT_URL is the source)
- declare RUN_RETENTION_DAYS=400, EGRESS_MODE=lax (no allow list: the OIDC
login source is not gated by the egress policy in 28.3.0), and
DISABLE_REGISTRATION=true (28's new default)
- document autoRollbackOnFailedHealth as inert (no ImagePolicy is rendered);
rollback is a PVC restore because the DB migration is one-way
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
PRE-MERGE OPERATOR CHECKLIST
gitea/gitea-data, and confirm the off-cluster backup (backup.policy: daily-offcluster) is recent. The 1.27 -> 28 DB migration (SQLite on that PVC) is one-way. I could not read the backup age from my worktree, so it is UNVERIFIED.GitRepositorysource. The Deployment isstrategy: Recreate, so while the new pod migrates and starts there is no Gitea at all: no CI runs, no source fetches, no git pushes. Do not queue other merges behind this one./approve-overrideis needed. No sensitive path (infra/gitea/approval-policy.json) is touched; the PR changescatalog/,apps/wave-0/,renovate.json5anddecisions/only.POST-MERGE CHECK
GET https://gitea.home.keithjackson.dev/api/v1/versionreturns28.3.0. Until then the apps-wave-0 Kustomization has not rolled.[gitea-oidc] login source 'keycloak' registered and verifiedfrom the postStart hook, and/data/gitea-oidc-bootstrap.logshould have no newFAILEDline.gate / gate (pull_request)) completes on theincluster-dindrunner. Opening or re-pushing any PR will start one.[security] ALLOWED_HOST_LIST/ egress warnings.Rollback
Restore
gitea-datafrom the pre-upgrade snapshot or backup, with the matching 1.27.3 image. Do NOT simply revert the image tag. 28 migrates the DB schema forward, and 1.27.3 running against a migrated DB is not a supported state. Nothing reverts the image automatically:update.autoRollbackOnFailedHealth: trueonly ever renders an annotation on a FluxImagePolicy, andapps/is rendered--no-image-automation(ci/render-golden.sh), so no ImagePolicy exists. The container has no probes, and a Deployment never self-reverts. I kept the flag (R-UPD-03) and added a comment in the catalog saying it is inert for this service and why.What this changes
catalog/gitea.yamlgitea/gitea:1.27.3->gitea/gitea:28.3.0;update.range">=1.22.0 <2.0.0"->">=28.0.0 <29.0.0"(the schema takes free-form range strings); env changes belowapps/wave-0/gitea.yamlrender.py --all catalog -o apps --wave-layout --no-image-automationin the toolbox. Only gitea changedrenovate.json5allowedVersions<2.0.0-><29.0.0, so Renovate proposes 28.x patches and minors (never auto-merged: gitea is on the blast-radius list) and never 29. The description no longer calls 28.x a stale tag. Checked withrenovate-config-validatorat 44.167.0 (the tagrenovate.yamlpins):Config validated successfullydecisions/2026-11-07-gitea-28.mdThe runner (
apps/wave-1/gitea-actions.yaml) and dind are not touched; they get separate PRs.28.3.0 is the newest stable 28.x tag on both Docker Hub (
28.0.0,28.1.0,28.2.0,28.3.0) and GitHub (v28.0.0..v28.3.0), checked 2026-11-07.Breaking changes and how each is handled
Sources: release notes https://blog.gitea.com/release-of-28.0.0/ , https://github.com/go-gitea/gitea/releases ; config reference https://docs.gitea.com/administration/config-cheat-sheet and
custom/conf/app.example.iniatv28.3.0; upgrade guide https://docs.gitea.com/installation/upgrade-from-gitea . Claims marked (source) were checked against the v28.3.0 Go source.[security] EGRESS_MODE(laxby default, orstrict) and[security] ALLOWED_HOST_LIST. In lax mode, public targets are allowed and private, loopback or CGNAT targets need an allow entry. (source) For those policies, a hostname entry does not admit a private address.WithLocalNeedsIPAllowrequires an IP or CIDR entry, or the built-inprivate/loopback(modules/egress/policy/policy.go).egress.NewSecurityPolicyhas exactly two callers: the OAuth2 avatar fetch (routers/web/auth/oauth.go;[oauth2_client] UPDATE_AVATARis off here) and OpenID 2.0 (modules/auth/openid, unused). The OIDC login source is goth'sopenidConnect.New(...)with its default HTTP client (services/auth/source/oauth2/providers_openid.go), so it never reaches the egress check.keycloak.${BASE_DOMAIN}resolving to the private Gateway address in-cluster therefore does not matter to it.uses:fetching (DEFAULT_ACTIONS_URL=github) is done by the runner, not by Gitea, so it is unaffected.GITEA__security__EGRESS_MODE=laxwith no allow list, on purpose. A hostname entry would not help for a private IP, and a CIDR entry would only widen webhook SSRF reach for no current consumer. The catalog comment says to add the narrowest CIDR (Gateway/32) only if a real private webhook target appears. This differs from the brief, which expected an allow-list entry. The source shows the premise does not hold for 28.3.0.[server] DOMAINis ignored (https://github.com/go-gitea/gitea/pull/39400). The domain and the SSH domain come fromROOT_URL, which was already set. (source)modules/setting/server.gono longer reads it; the only remainingDOMAINkey is[session]. Grepping the repo found no other reader ofGITEA__server__DOMAIN;bootstrap/gitea/bootstrap-gitea.shsets only ROOT_URL and SSH_PORT. Handled by: removing the env line. The old value thatenvironment-to-inialready wrote intoapp.inistays there and is inert.cleanup_action_runscron. (source)modules/setting/actions.go:defaultRunRetentionDays = 400. The spec repo has no CI-history retention requirement. Handled by: declaringGITEA__actions__RUN_RETENTION_DAYS=400(upstream's value, now explicit).routers/web/auth/oauth.go) depends onALLOW_ONLY_INTERNAL_REGISTRATIONandENABLE_AUTO_REGISTRATION, not onDISABLE_REGISTRATION, so Keycloak auto-provisioning keeps working. Handled by: declaringGITEA__service__DISABLE_REGISTRATION=true. Accounts now arrive only through Keycloak or the CLI.Dockerfileat v28.3.0), so no action is needed.admin user list --adminprintsID Username Email IsActive IsAdmin 2FA Type, soawk '{print $2}'is still the username.admin auth listprintsID Name Type Enabled, so$1/$2are still right.add-oauthandupdate-oauthkeep--name --provider --key --secret --auto-discover-url --scopes --group-claim-name --admin-group --id.admin user createkeeps--username --password --email --admin --must-change-password.curlandsu-execare still in the image, and the s6setupstill runsenvironment-to-ini(so theGITEA__*env vars still apply).if:is evaluated before matrix expansion,fail-fastis enforced, and private reusable workflows are restricted. No workflow under.gitea/workflows/usesmatrixorstrategy, so nothing changes./-/ws(#36965). If the Gateway does not forward the upgrade, notifications fall back to polling, so this is not a break. UNVERIFIED on the Envoy Gateway.timeout: 5m, wait: true. A migration longer than that would mark the wave unhealthy for a while, but it would not interrupt the migration.Local gate (toolbox, committed tree
8a9baa9)DEV_KUBECONFIG=/dev/null ENGINE=<.git-mount shim> ./infra/devcontainer/dev.sh run bash -c './ci/gate.sh'-> rc 3 (UNDETERMINED), not green.spec-immutability pass · decisions-log pass (76 entries; 75 on main + this one, which shows the gate saw the commit) · lint pass · l0 pass · render-golden pass · l1 pass (3/3) · approval-policy pass (N/A offline) · gitea-settings SKIPPED (no
SETTINGS_API_TOKEN; the only stage that did not run) · conformance L2/L3 SKIPPED (no cluster, Phase 0).Not verified
🤖 Generated with Claude Code