diff --git a/CHANGELOG.md b/CHANGELOG.md index 2607851ff..a8bc861c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,18 @@ Hive did not historically maintain a complete changelog. This file starts a prag ## Unreleased +## 2026-09-09 (v4.20.2) + +### Fixed + +- `ContributeWSHub` now exposes an explicit lifecycle (`Close()`/`Stop()`) with prompt background `cleanupLoop` shutdown, preventing leaked cleanup goroutines in tests and ensuring server shutdown terminates background hub sweeps deterministically ([#6272](https://github.com/hivecommons/hive/issues/6272)). Background lease pruning and stale-connection sweeps stop immediately when the hub is closed, eliminating race conditions where leaked test loops touched state rooted in already-removed temporary test directories. +- Fixed a goroutine leak in `pkg/proxy` tests (`TestProxyHTTPResponseWriteError` and `TestProxyHTTPUpstreamReadError`) by explicitly tracking and joining all goroutines via `sync.WaitGroup` and channel synchronization, and added per-instance `bodyStallTimeout` configuration on `GitHubProxy` to prevent races with test-time timeout modifications ([#6338](https://github.com/hivecommons/hive/issues/6338)). +- Added test injection seam and regression coverage for dashboard branding file ownership verification and HIVE_BRANDING_ALLOW_UNSAFE_OWNER escape hatch ([#6341](https://github.com/hivecommons/hive/issues/6341)). +- Terminal API error responses on `/api/` paths now serve `Content-Type: application/json` instead of `text/plain; charset=utf-8` ([#6344](https://github.com/hivecommons/hive/issues/6344)). Previously, `writeTerminalRoleForbidden`, `writeQueryTokenRejected`, and error branches in `handleCreateTerminalHandoff` called `http.Error`, which unconditionally overwrote the content type to plain text despite sending JSON-shaped error bodies. +- The fleet row's "N behind" badge, its "Queued for auto-upgrade" pill and the channel-lag health verdict now measure a spoke against the target its own image tag can actually deliver, not against the v4 branch tip. A spoke on `:stable` is compared with the commit `:stable` currently carries (the same resolution the auto-upgrade engine uses), so a fleet that is fully current on its channel no longer reads as "128 behind · Queued" while the hub, correctly, refuses to move it; the branch-tip distance still appears in the badge tooltip so the operator can see how stale the channel itself has become. The "Upgrade now" confirmation names the reachable commit for the same reason. Older hubs that do not send the new `behindTargetSHA`/`commitsBehindTarget` fields fall back to the previous branch-tip count. +- The `just contribute-hive claude local` sandbox no longer emits dead `Write(path)` permission rules. Claude Code consults only `Edit(path)` rules for file-permission checks — a `Write(...)` path rule is accepted but never matched, and warns at startup — while `Edit` rules already cover every built-in file-modifying tool, Write included. Together with the `//`-absolute form (`Edit(//…/workspace/**)`; a **single** leading `/` anchors at the settings source, not the filesystem root, so the rule silently matched nothing under `--permission-mode dontAsk`), the file-tool write grants now match the workspace and the agent cwd exactly. Bash writes always worked (the OS sandbox's `filesystem.allowWrite` takes real paths), which is what masked this. +- The hub's self-upgrade target no longer freezes when the branch tip has no `hive-hub` image. Release-bump commits (`release: vX.Y.Z`) are merged by `github-actions[bot]` with `GITHUB_TOKEN`, which fires no `push` workflows, so `docker.yml` never builds an image for them; and a commit that is tip for less than one two-minute poll never gets verified as tip at all. Either way the poller only ever admitted the *tip* into `latest_hub_shas`, so the target stuck at the last tip it had verified — on 2026-09-09 the hub sat on `bd68d95` for 13 hours with `d566f97` published one commit ahead, the badge showed "behind", and the manual **Upgrade** button returned an error because there was nothing newer to roll to. When the tip's hub image is absent, the poller now walks the branch's recent commits newest-first and targets the newest one whose hub image *is* on GHCR, stopping at the current target so advancement stays monotonic. Same idea as the spoke-side reachable-target fix in #6294, applied to the hub. + ## 2026-09-09 (v4.20.1) ### Fixed diff --git a/changelog.d/fixed-6272-contribute-ws-hub-cleanup-lifecycle.md b/changelog.d/fixed-6272-contribute-ws-hub-cleanup-lifecycle.md deleted file mode 100644 index 94880da3d..000000000 --- a/changelog.d/fixed-6272-contribute-ws-hub-cleanup-lifecycle.md +++ /dev/null @@ -1 +0,0 @@ -- `ContributeWSHub` now exposes an explicit lifecycle (`Close()`/`Stop()`) with prompt background `cleanupLoop` shutdown, preventing leaked cleanup goroutines in tests and ensuring server shutdown terminates background hub sweeps deterministically ([#6272](https://github.com/hivecommons/hive/issues/6272)). Background lease pruning and stale-connection sweeps stop immediately when the hub is closed, eliminating race conditions where leaked test loops touched state rooted in already-removed temporary test directories. diff --git a/changelog.d/fixed-6338-proxy-race-timeout-leak.md b/changelog.d/fixed-6338-proxy-race-timeout-leak.md deleted file mode 100644 index 884e3c38a..000000000 --- a/changelog.d/fixed-6338-proxy-race-timeout-leak.md +++ /dev/null @@ -1 +0,0 @@ -- Fixed a goroutine leak in `pkg/proxy` tests (`TestProxyHTTPResponseWriteError` and `TestProxyHTTPUpstreamReadError`) by explicitly tracking and joining all goroutines via `sync.WaitGroup` and channel synchronization, and added per-instance `bodyStallTimeout` configuration on `GitHubProxy` to prevent races with test-time timeout modifications ([#6338](https://github.com/hivecommons/hive/issues/6338)). diff --git a/changelog.d/fixed-6341-branding-owner-guard-seam.md b/changelog.d/fixed-6341-branding-owner-guard-seam.md deleted file mode 100644 index 9b005bbf2..000000000 --- a/changelog.d/fixed-6341-branding-owner-guard-seam.md +++ /dev/null @@ -1 +0,0 @@ -- Added test injection seam and regression coverage for dashboard branding file ownership verification and HIVE_BRANDING_ALLOW_UNSAFE_OWNER escape hatch ([#6341](https://github.com/hivecommons/hive/issues/6341)). diff --git a/changelog.d/fixed-6344-terminal-api-error-content-type.md b/changelog.d/fixed-6344-terminal-api-error-content-type.md deleted file mode 100644 index 4cb4a95bc..000000000 --- a/changelog.d/fixed-6344-terminal-api-error-content-type.md +++ /dev/null @@ -1 +0,0 @@ -- Terminal API error responses on `/api/` paths now serve `Content-Type: application/json` instead of `text/plain; charset=utf-8` ([#6344](https://github.com/hivecommons/hive/issues/6344)). Previously, `writeTerminalRoleForbidden`, `writeQueryTokenRejected`, and error branches in `handleCreateTerminalHandoff` called `http.Error`, which unconditionally overwrote the content type to plain text despite sending JSON-shaped error bodies. diff --git a/changelog.d/fixed-behind-count-per-channel.md b/changelog.d/fixed-behind-count-per-channel.md deleted file mode 100644 index 34a982705..000000000 --- a/changelog.d/fixed-behind-count-per-channel.md +++ /dev/null @@ -1 +0,0 @@ -- The fleet row's "N behind" badge, its "Queued for auto-upgrade" pill and the channel-lag health verdict now measure a spoke against the target its own image tag can actually deliver, not against the v4 branch tip. A spoke on `:stable` is compared with the commit `:stable` currently carries (the same resolution the auto-upgrade engine uses), so a fleet that is fully current on its channel no longer reads as "128 behind · Queued" while the hub, correctly, refuses to move it; the branch-tip distance still appears in the badge tooltip so the operator can see how stale the channel itself has become. The "Upgrade now" confirmation names the reachable commit for the same reason. Older hubs that do not send the new `behindTargetSHA`/`commitsBehindTarget` fields fall back to the previous branch-tip count. diff --git a/changelog.d/fixed-claude-local-write-roots-absolute.md b/changelog.d/fixed-claude-local-write-roots-absolute.md deleted file mode 100644 index 745361bb6..000000000 --- a/changelog.d/fixed-claude-local-write-roots-absolute.md +++ /dev/null @@ -1 +0,0 @@ -- The `just contribute-hive claude local` sandbox no longer emits dead `Write(path)` permission rules. Claude Code consults only `Edit(path)` rules for file-permission checks — a `Write(...)` path rule is accepted but never matched, and warns at startup — while `Edit` rules already cover every built-in file-modifying tool, Write included. Together with the `//`-absolute form (`Edit(//…/workspace/**)`; a **single** leading `/` anchors at the settings source, not the filesystem root, so the rule silently matched nothing under `--permission-mode dontAsk`), the file-tool write grants now match the workspace and the agent cwd exactly. Bash writes always worked (the OS sandbox's `filesystem.allowWrite` takes real paths), which is what masked this. diff --git a/changelog.d/fixed-hub-upgrade-target-walkback.md b/changelog.d/fixed-hub-upgrade-target-walkback.md deleted file mode 100644 index afc04750a..000000000 --- a/changelog.d/fixed-hub-upgrade-target-walkback.md +++ /dev/null @@ -1 +0,0 @@ -- The hub's self-upgrade target no longer freezes when the branch tip has no `hive-hub` image. Release-bump commits (`release: vX.Y.Z`) are merged by `github-actions[bot]` with `GITHUB_TOKEN`, which fires no `push` workflows, so `docker.yml` never builds an image for them; and a commit that is tip for less than one two-minute poll never gets verified as tip at all. Either way the poller only ever admitted the *tip* into `latest_hub_shas`, so the target stuck at the last tip it had verified — on 2026-09-09 the hub sat on `bd68d95` for 13 hours with `d566f97` published one commit ahead, the badge showed "behind", and the manual **Upgrade** button returned an error because there was nothing newer to roll to. When the tip's hub image is absent, the poller now walks the branch's recent commits newest-first and targets the newest one whose hub image *is* on GHCR, stopping at the current target so advancement stays monotonic. Same idea as the spoke-side reachable-target fix in #6294, applied to the hub.