diff --git a/changelog.d/fixed-10697.md b/changelog.d/fixed-10697.md
new file mode 100644
index 0000000000..b231608762
--- /dev/null
+++ b/changelog.d/fixed-10697.md
@@ -0,0 +1 @@
+- dashboard: Overview 'Actionable now' shows the exact reconciling equation; drop median-age tile (#10697)
diff --git a/src/docs/api-reference.md b/src/docs/api-reference.md
index 4857d06a6d..5b8e494798 100644
--- a/src/docs/api-reference.md
+++ b/src/docs/api-reference.md
@@ -10,17 +10,17 @@ Auth levels are derived from dashboard middleware (`isPublicPath`, dashboard tok
|---|---|---|---|---|
| `GET` | `/api/version` | Dashboard auth/session | Build/version metadata; includes `upgradeMarker` (`target`, `current`, `attempts`, `maxAttempts`, `failed`, `requestedAt`, `lastError`) while a self-upgrade is in flight or has failed ([#6765](https://github.com/hivecommons/hive/issues/6765)), the hub-delivered `upgradePolicy` (`hub_managed`, `spoke_managed`, `schedule`, `schedule_hour`, `schedule_timezone`, `schedule_weekday`, `paused`, `branch`, `channel`, `target_sha`, `target_resolved`, `armed_target`, and `next_update_at` — the hub's RFC3339 ETA for the next promotion into the `stable` channel, omitted when unknown ([#10256](https://github.com/hivecommons/hive/issues/10256))) when available, and an `autoUpdate` object (`enabled`, `state` — one of `disabled`/`up_to_date`/`behind`/`retrying`/`failed`/`unknown` — `healthy`, `period`, `targetBranch`, `targetCommit`, `currentCommit`, `commitsBehind`, `lastAttemptAt`, `lastError`, `detail`, and `nextUpdateAt` — the hub's RFC3339 ETA for the next update, omitted when unknown, disabled or paused ([#10257](https://github.com/hivecommons/hive/issues/10257))) that never reports a failed or unknown update as healthy ([#6962](https://github.com/hivecommons/hive/issues/6962), [#6963](https://github.com/hivecommons/hive/issues/6963)) | `pkg/dashboard/api.go:36` |
| `POST` | `/api/release-channel` | Owner only | Hosted spoke self-service release-channel selector; relays `stable`/`candidate`/`edge` to the hub's existing switch-branch endpoint with the spoke dashboard-token proof, and reports the requested channel as pending until the Deployment image lands | `pkg/dashboard/api.go:57` |
-| `GET` | `/api/health` | Public | Basic health probe | `pkg/dashboard/server.go:1213` |
-| `GET` | `/api/health/deep` | Public | Deep health probe | `pkg/dashboard/server.go:1214` |
-| `GET` | `/api/livez` | Public | Kubernetes liveness probe | `pkg/dashboard/server.go:1215` |
-| `GET` | `/metrics` | Registered only when `HIVE_METRICS_ENABLED`; requires `Authorization: ****** (403 if the token is unset) | Prometheus metrics | `pkg/dashboard/server.go:1221` |
-| `GET` | `/api/status` | Dashboard auth/session | Dashboard aggregate status; accepts `?fields=a,b` for top-level selection and `?omit=repos` to drop heavy sections, and honors `Accept-Encoding: gzip`; `agents[]` includes busy-pane visibility fields (`kicksUndeliverable`, `busySince`, `lastTranscriptActivity`, `condition`, `conditionMessage`) when a Working pane cannot accept kicks | `pkg/dashboard/server.go:1226` |
-| `GET` | `/api/status/summary` | Dashboard auth/session | Compact agent/governor/budget status summary for pollers; honors `Accept-Encoding: gzip` | `pkg/dashboard/server.go:1227` |
-| `GET` | `/api/overview/issues.csv` | Dashboard auth/session | Cached Overview issue-band export as browser-compatible CSV; filters `band`, `repo`/`repos`, `stale`, and `held` are ANDed. | `pkg/dashboard/server.go:1228` |
-| `GET` | `/api/overview/issues.json` | Dashboard auth/session | Cached Overview issue-band export as typed JSON with `bands[]` metadata and row fields matching the CSV columns. | `pkg/dashboard/server.go:1229` |
-| `GET` | `/api/overview/prs.csv` | Dashboard auth/session | Cached Overview PR-band export as browser-compatible CSV; filters `band`, `repo`/`repos`, `stale`, and `held` are ANDed. | `pkg/dashboard/server.go:1230` |
-| `GET` | `/api/overview/prs.json` | Dashboard auth/session | Cached Overview PR-band export as typed JSON with `bands[]` metadata and row fields matching the CSV columns. | `pkg/dashboard/server.go:1231` |
-| `GET` | `/api/events` | Dashboard auth/session | Server-sent event stream | `pkg/dashboard/server.go:1232` |
+| `GET` | `/api/health` | Public | Basic health probe | `pkg/dashboard/server.go:1232` |
+| `GET` | `/api/health/deep` | Public | Deep health probe | `pkg/dashboard/server.go:1233` |
+| `GET` | `/api/livez` | Public | Kubernetes liveness probe | `pkg/dashboard/server.go:1234` |
+| `GET` | `/metrics` | Registered only when `HIVE_METRICS_ENABLED`; requires `Authorization: ****** (403 if the token is unset) | Prometheus metrics | `pkg/dashboard/server.go:1240` |
+| `GET` | `/api/status` | Dashboard auth/session | Dashboard aggregate status; accepts `?fields=a,b` for top-level selection and `?omit=repos` to drop heavy sections, and honors `Accept-Encoding: gzip`; `agents[]` includes busy-pane visibility fields (`kicksUndeliverable`, `busySince`, `lastTranscriptActivity`, `condition`, `conditionMessage`) when a Working pane cannot accept kicks | `pkg/dashboard/server.go:1245` |
+| `GET` | `/api/status/summary` | Dashboard auth/session | Compact agent/governor/budget status summary for pollers; honors `Accept-Encoding: gzip` | `pkg/dashboard/server.go:1246` |
+| `GET` | `/api/overview/issues.csv` | Dashboard auth/session | Cached Overview issue-band export as browser-compatible CSV; filters `band`, `repo`/`repos`, `stale`, and `held` are ANDed. | `pkg/dashboard/server.go:1247` |
+| `GET` | `/api/overview/issues.json` | Dashboard auth/session | Cached Overview issue-band export as typed JSON with `bands[]` metadata and row fields matching the CSV columns. | `pkg/dashboard/server.go:1248` |
+| `GET` | `/api/overview/prs.csv` | Dashboard auth/session | Cached Overview PR-band export as browser-compatible CSV; filters `band`, `repo`/`repos`, `stale`, and `held` are ANDed. | `pkg/dashboard/server.go:1249` |
+| `GET` | `/api/overview/prs.json` | Dashboard auth/session | Cached Overview PR-band export as typed JSON with `bands[]` metadata and row fields matching the CSV columns. | `pkg/dashboard/server.go:1250` |
+| `GET` | `/api/events` | Dashboard auth/session | Server-sent event stream | `pkg/dashboard/server.go:1251` |
| `GET` | `/api/swarm` | Dashboard auth/session | Current swarm status: display name, duration, active repo, expiry, persisted prep metrics when available, and idle-unlock fields. | `pkg/dashboard/swarm.go:310` |
| `POST` | `/api/swarm` | Owner only | Start a 24h repo swarm for a configured repo, returning 409 when another swarm is active and 423 when a previous swarm keeps the next swarm locked until enough agents are idle unless body `{"repo":"owner/name","force":true}` is used; saves the active record before repo prep, persists prep metrics, and announces the start to Discord when configured. | `pkg/dashboard/swarm.go:311` |
| `DELETE` | `/api/swarm` | Owner only | End the active swarm, score issues closed and PRs merged in the window, append it to history, and announce the result to Discord when configured. | `pkg/dashboard/swarm.go:312` |
@@ -62,7 +62,7 @@ Auth levels are derived from dashboard middleware (`isPublicPath`, dashboard tok
| Method | Path | Auth | Purpose | Source |
|---|---|---|---|---|
| `GET` | `/api/style` | Public | Sanitized custom dashboard CSS | `pkg/dashboard/api.go:37` |
-| `GET` | `/branding/custom.css` | Dashboard auth/session | Operator branding stylesheet override, read per request (see [branding](branding.md)) | `pkg/dashboard/server.go:1293` |
+| `GET` | `/branding/custom.css` | Dashboard auth/session | Operator branding stylesheet override, read per request (see [branding](branding.md)) | `pkg/dashboard/server.go:1312` |
| `GET` | `/api/snapshot/frame-ancestors` | Public | Snapshot framing allowlist | `pkg/dashboard/api.go:70` |
| `GET` | `/api/snapshot` | Public | Snapshot data | `pkg/dashboard/api.go:71` |
| `GET` | `/snapshot` | Public | Public read-only snapshot page | `pkg/dashboard/api.go:72` |
@@ -90,9 +90,9 @@ Auth levels are derived from dashboard middleware (`isPublicPath`, dashboard tok
| `GET` | `/api/openrouter/models` | Dashboard auth/session | Open Router Models | `pkg/dashboard/openrouter.go:47` |
| `GET` | `/api/openrouter/credit` | Dashboard auth/session | Open Router Credit | `pkg/dashboard/openrouter.go:48` |
| `GET` | `/openrouter/callback` | Public | Open Router Callback | `pkg/dashboard/openrouter.go:49` |
-| `POST` | `/api/github-app/recheck` | Dashboard auth/session | GitHub App Recheck | `pkg/dashboard/server.go:1233` |
-| `POST` | `/api/github-app/install-clicked` | Dashboard auth/session | GitHub App Install Clicked | `pkg/dashboard/server.go:1234` |
-| `GET` | `/gh-setup` | Public | GitHub App Setup Callback | `pkg/dashboard/server.go:1235` |
+| `POST` | `/api/github-app/recheck` | Dashboard auth/session | GitHub App Recheck | `pkg/dashboard/server.go:1252` |
+| `POST` | `/api/github-app/install-clicked` | Dashboard auth/session | GitHub App Install Clicked | `pkg/dashboard/server.go:1253` |
+| `GET` | `/gh-setup` | Public | GitHub App Setup Callback | `pkg/dashboard/server.go:1254` |
## Configuration
@@ -539,7 +539,7 @@ always resolved server-side from the validated token.
| `POST` | `/api/hives/{id}/heartbeat` | Dashboard auth/session | Hives Heartbeat | `pkg/dashboard/api_contribute.go:263` |
| `DELETE` | `/api/hives/{id}` | Owner only | Hives Delete | `pkg/dashboard/api_contribute.go:264` |
| `POST` | `/api/hives/onboard` | Dashboard auth/session | Hives Onboard | `pkg/dashboard/api_contribute.go:265` |
-| `GET` | `/sso` | Public | SSO | `pkg/dashboard/server.go:1240` |
+| `GET` | `/sso` | Public | SSO | `pkg/dashboard/server.go:1259` |
## Hub SaaS
diff --git a/src/docs/dashboard.md b/src/docs/dashboard.md
index c41b559698..52e97d25b3 100644
--- a/src/docs/dashboard.md
+++ b/src/docs/dashboard.md
@@ -61,6 +61,14 @@ posture, and cadence controls for the hive. Its collapsible **PRs by model** nes
sub-section reads `GET /api/governor/pr-models` with the selected `7d`, `30d`,
or `all` window. The nested header keeps the window/sort toggles visible and its
collapsed summary shows the current top-ranked model plus model count.
+
+The Governor **actionable issues** and **actionable PRs** tiles use the shared
+server-side actionable partition described in [Repository card legend, issue
+bands, and PR bands](#repository-card-legend-issue-bands-and-pr-bands). Their
+subtitles show the per-kind reconciliation from total open work to the
+actionable split, and those two results always add back to the Overview
+**Actionable now** total.
+
The cadence table starts each agent row with the same `1`/`0` Agent power
rocker used in the agent settings panel, so owners can enable or disable an
agent from the table; disabled rows are dimmed, and read-only viewers see the
@@ -196,18 +204,21 @@ client-side issue and PR bands across the selected repository view. Its SVG
charts reuse the repository-card classifiers for actionable plus held
issues/PRs, so their totals match the visible band counters and respect the
Overview settings repo filter without a separate API call. A compact KPI strip
-shows total open issues, total open PRs, actionable now, held,
-blocked/needs-human, and the median actionable age. **Total open issues** and
-**Total open PRs** are forge totals for the selected configured repositories
-from the existing scanner tick: open GitHub issues exclude pull requests, while
-open GitHub pull requests include drafts. Their muted sub-lines name how many
-items are present in the Overview's actionable/held bands and how many were
-outside those bands because of hard-suppress labels, exempt filters, reporter
-triage, standing meta issues, dependency dashboards, drafts, or other scanner
-filters. **Actionable now** is the shared server-side count used by
-the Overview KPI and the Governor collapsed split: open enumerated issues plus
-open enumerated PRs after hold/exempt filters, excluding items in the waiting,
-done, draft, or blocked Overview bands. Its `actionableNow.issues` and
+shows total open issues, total open PRs, actionable now, held, and
+blocked/needs-human. This is the same partition used by the Governor
+actionable issue/PR subtitles. **Total open issues** and **Total open PRs** are
+forge totals for the selected configured repositories from the existing scanner tick:
+open GitHub issues exclude pull requests, while open GitHub pull requests
+include drafts. Their subtitles stay empty so the reconciliation is not split
+across tiles. **Actionable now** is the shared server-side count used by the
+Overview KPI and the Governor collapsed split: open enumerated issues plus open
+enumerated PRs after hold/exempt filters, excluding items in the waiting, done,
+draft, or blocked Overview bands. Its subtitle carries the complete partition,
+for example `41 issues + 17 PRs = 13 actionable + 13 held + 10
+blocked/needs-human + 22 outside`; the held and blocked/needs-human tiles use
+the same server-side terms, and outside covers scanner-filtered work such as
+hard-suppress labels other than needs-human, exempt filters, reporter triage, standing meta issues,
+dependency dashboards, drafts, or other filters. Its `actionableNow.issues` and
`actionableNow.prs` splits always sum to `actionableNow.total`. Operators can
view each Issues or PRs panel as a donut,
pie, horizontal bar, single 100% stacked bar, line/spark trend, or age
@@ -231,12 +242,12 @@ pages include their API token in the download link's query string.
The Overview header's ⚙️ popover stores browser-local chart preferences under
`hive-overview-charts`: which chart types are in rotation, whether the carousel
is enabled, the 5-second to 5-minute interval, transition style, duration, donut
-label mode, KPI visibility, export format, default age basis, and the bounded
+label mode, KPI visibility, export format, and the bounded
client-side line/spark history. The repo multi-select is stored separately under
`hive.overview.repos`, with All/None shortcuts and an Org shortcut when the
hive spans multiple GitHub organizations. The default remains donut-only with
the carousel off, a 30-second interval, fade transition, normal duration, KPI
-strip on, CSV exports, updated-time age basis, and all repos selected.
+strip on, CSV exports, and all repos selected.
Manual arrows and dot indicators are available even when timed rotation is off;
timed rotation pauses while the panel is hovered or the tab is hidden, and
reduced-motion users get instant swaps.
diff --git a/src/pkg/dashboard/actionable_now_10561_test.go b/src/pkg/dashboard/actionable_now_10561_test.go
index b101fb0604..9c8169c140 100644
--- a/src/pkg/dashboard/actionable_now_10561_test.go
+++ b/src/pkg/dashboard/actionable_now_10561_test.go
@@ -45,6 +45,9 @@ func TestOverviewAndGovernorBindSharedActionableNowField10561(t *testing.T) {
"const sharedActionable = (data && data.actionableNow) || {};",
"sharedActionable.issues",
"sharedActionable.prs",
+ "sharedActionable.issueEquation",
+ "sharedActionable.prEquation",
+ "issueEquationText ? `
What sh
const autoTitle = state.carousel ? 'Auto-play on — rotates every ' + Math.round(overviewCarouselDwellMs(state) / 1000) + 's through the ticked charts. Click to pause.' : 'Auto-play off — click to rotate through the ticked charts every ' + Math.round(overviewCarouselDwellMs(state) / 1000) + 's';
return `
What sh
rerenderOverviewChartsFromState(state);
}
- function setOverviewTimeBasis(value) {
- const state = overviewChartsState();
- if (OVERVIEW_TIME_BASES.includes(value)) state.timeBasis = value;
- rerenderOverviewChartsFromState(state);
- }
-
function setOverviewKPIs(checked) {
const state = overviewChartsState();
state.showKPIs = !!checked;