From ddec7b83689198144d7e91530c4278c869415fdd Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 00:08:22 +0000 Subject: [PATCH 1/3] docs(specs): reopen spec 022 for the version trend amendment Move specs/done_022-default-telemetry-dashboard back to specs/022-default-telemetry-dashboard and update in-repo references. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- api/internal/telemetry/destination.go | 2 +- api/internal/telemetry/telemetry.go | 2 +- assets/design-export/MANIFEST.md | 2 +- .../OPEN-DECISIONS.md | 0 .../checklists/requirements.md | 0 .../contracts/api-telemetry-http.md | 0 .../contracts/install-config.md | 0 .../contracts/receiver-http.md | 0 .../contracts/report-schema.md | 0 .../data-model.md | 0 .../plan.md | 4 ++-- .../quickstart.md | 0 .../research.md | 0 .../spec.md | 0 .../tasks.md | 10 +++++----- telemetry-receiver/specs.md | 4 ++-- telemetryschema/doc.go | 2 +- telemetryschema/specs.md | 4 ++-- 18 files changed, 15 insertions(+), 15 deletions(-) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/OPEN-DECISIONS.md (100%) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/checklists/requirements.md (100%) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/contracts/api-telemetry-http.md (100%) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/contracts/install-config.md (100%) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/contracts/receiver-http.md (100%) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/contracts/report-schema.md (100%) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/data-model.md (100%) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/plan.md (99%) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/quickstart.md (100%) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/research.md (100%) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/spec.md (100%) rename specs/{done_022-default-telemetry-dashboard => 022-default-telemetry-dashboard}/tasks.md (98%) diff --git a/api/internal/telemetry/destination.go b/api/internal/telemetry/destination.go index e5f8b3c7e..ed3e184ce 100644 --- a/api/internal/telemetry/destination.go +++ b/api/internal/telemetry/destination.go @@ -7,7 +7,7 @@ import ( ) // DefaultEndpoint is the project default telemetry destination, ruled in -// specs/done_022-default-telemetry-dashboard/OPEN-DECISIONS.md OD-1. It must stay +// specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md OD-1. It must stay // an https URL (hack/check-telemetry-default.sh enforces this in CI). const DefaultEndpoint = "https://telemetry.gameplane.net/ingest" diff --git a/api/internal/telemetry/telemetry.go b/api/internal/telemetry/telemetry.go index bb39a105d..f040b6951 100644 --- a/api/internal/telemetry/telemetry.go +++ b/api/internal/telemetry/telemetry.go @@ -1,5 +1,5 @@ // Package telemetry sends the install's usage report to its resolved -// destination (specs/done_022-default-telemetry-dashboard). +// destination (specs/022-default-telemetry-dashboard). // // Privacy: the basic report carries only the control-plane version and // total counts. The extended report adds an install ID and environment diff --git a/assets/design-export/MANIFEST.md b/assets/design-export/MANIFEST.md index 06b8a43cd..cea26e8ce 100644 --- a/assets/design-export/MANIFEST.md +++ b/assets/design-export/MANIFEST.md @@ -2502,7 +2502,7 @@ Server Settings gets a "Game configuration" section (edit `spec.config` from the ## Spec 022 telemetry notice and settings (2026-10-07) -T041 and T052 of `specs/done_022-default-telemetry-dashboard/`. New frames sit in an empty region below the Admin Settings row (x 11480–21120, y 25404–28584); no existing frame changed. Destination hosts are samples while OD-1 is unruled: their text nodes are named `host (SAMPLE, OD-1 unruled)` (default) and `host (SAMPLE)` (bundled). +T041 and T052 of `specs/022-default-telemetry-dashboard/`. New frames sit in an empty region below the Admin Settings row (x 11480–21120, y 25404–28584); no existing frame changed. Destination hosts are samples while OD-1 is unruled: their text nodes are named `host (SAMPLE, OD-1 unruled)` (default) and `host (SAMPLE)` (bundled). | Object ID | Name | Change | | --- | --- | --- | diff --git a/specs/done_022-default-telemetry-dashboard/OPEN-DECISIONS.md b/specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md similarity index 100% rename from specs/done_022-default-telemetry-dashboard/OPEN-DECISIONS.md rename to specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md diff --git a/specs/done_022-default-telemetry-dashboard/checklists/requirements.md b/specs/022-default-telemetry-dashboard/checklists/requirements.md similarity index 100% rename from specs/done_022-default-telemetry-dashboard/checklists/requirements.md rename to specs/022-default-telemetry-dashboard/checklists/requirements.md diff --git a/specs/done_022-default-telemetry-dashboard/contracts/api-telemetry-http.md b/specs/022-default-telemetry-dashboard/contracts/api-telemetry-http.md similarity index 100% rename from specs/done_022-default-telemetry-dashboard/contracts/api-telemetry-http.md rename to specs/022-default-telemetry-dashboard/contracts/api-telemetry-http.md diff --git a/specs/done_022-default-telemetry-dashboard/contracts/install-config.md b/specs/022-default-telemetry-dashboard/contracts/install-config.md similarity index 100% rename from specs/done_022-default-telemetry-dashboard/contracts/install-config.md rename to specs/022-default-telemetry-dashboard/contracts/install-config.md diff --git a/specs/done_022-default-telemetry-dashboard/contracts/receiver-http.md b/specs/022-default-telemetry-dashboard/contracts/receiver-http.md similarity index 100% rename from specs/done_022-default-telemetry-dashboard/contracts/receiver-http.md rename to specs/022-default-telemetry-dashboard/contracts/receiver-http.md diff --git a/specs/done_022-default-telemetry-dashboard/contracts/report-schema.md b/specs/022-default-telemetry-dashboard/contracts/report-schema.md similarity index 100% rename from specs/done_022-default-telemetry-dashboard/contracts/report-schema.md rename to specs/022-default-telemetry-dashboard/contracts/report-schema.md diff --git a/specs/done_022-default-telemetry-dashboard/data-model.md b/specs/022-default-telemetry-dashboard/data-model.md similarity index 100% rename from specs/done_022-default-telemetry-dashboard/data-model.md rename to specs/022-default-telemetry-dashboard/data-model.md diff --git a/specs/done_022-default-telemetry-dashboard/plan.md b/specs/022-default-telemetry-dashboard/plan.md similarity index 99% rename from specs/done_022-default-telemetry-dashboard/plan.md rename to specs/022-default-telemetry-dashboard/plan.md index 30d699e28..1d1984f45 100644 --- a/specs/done_022-default-telemetry-dashboard/plan.md +++ b/specs/022-default-telemetry-dashboard/plan.md @@ -2,7 +2,7 @@ **Branch**: `feat/default-telemetry-dashboard` (not yet created; the spec-kit script derived `022-default-telemetry-dashboard`) | **Date**: 2026-10-06 | **Spec**: [spec.md](spec.md) -**Input**: Feature specification from `specs/done_022-default-telemetry-dashboard/spec.md` +**Input**: Feature specification from `specs/022-default-telemetry-dashboard/spec.md` ## Summary @@ -109,7 +109,7 @@ No NEEDS CLARIFICATION remains. Every technical unknown is resolved in [research ### Documentation (this feature) ```text -specs/done_022-default-telemetry-dashboard/ +specs/022-default-telemetry-dashboard/ ├── spec.md ├── OPEN-DECISIONS.md # OD-1..OD-5 ruled ├── plan.md # this file diff --git a/specs/done_022-default-telemetry-dashboard/quickstart.md b/specs/022-default-telemetry-dashboard/quickstart.md similarity index 100% rename from specs/done_022-default-telemetry-dashboard/quickstart.md rename to specs/022-default-telemetry-dashboard/quickstart.md diff --git a/specs/done_022-default-telemetry-dashboard/research.md b/specs/022-default-telemetry-dashboard/research.md similarity index 100% rename from specs/done_022-default-telemetry-dashboard/research.md rename to specs/022-default-telemetry-dashboard/research.md diff --git a/specs/done_022-default-telemetry-dashboard/spec.md b/specs/022-default-telemetry-dashboard/spec.md similarity index 100% rename from specs/done_022-default-telemetry-dashboard/spec.md rename to specs/022-default-telemetry-dashboard/spec.md diff --git a/specs/done_022-default-telemetry-dashboard/tasks.md b/specs/022-default-telemetry-dashboard/tasks.md similarity index 98% rename from specs/done_022-default-telemetry-dashboard/tasks.md rename to specs/022-default-telemetry-dashboard/tasks.md index 2199fec94..790cc08d9 100644 --- a/specs/done_022-default-telemetry-dashboard/tasks.md +++ b/specs/022-default-telemetry-dashboard/tasks.md @@ -5,7 +5,7 @@ description: "Task list for feature 022: default telemetry destination, extended # Tasks: Default telemetry destination, extended telemetry, and telemetry dashboard -**Input**: Design documents from `specs/done_022-default-telemetry-dashboard/` +**Input**: Design documents from `specs/022-default-telemetry-dashboard/` **Prerequisites**: [plan.md](plan.md), [spec.md](spec.md), [research.md](research.md), [data-model.md](data-model.md), [contracts/](contracts/), [quickstart.md](quickstart.md), [OPEN-DECISIONS.md](OPEN-DECISIONS.md) @@ -33,7 +33,7 @@ description: "Task list for feature 022: default telemetry destination, extended **Purpose**: Create the branch, get test sign-off, and wire the new `telemetryschema` module into the build. -- [X] T001 Create branch `feat/default-telemetry-dashboard` from `master` and commit `specs/done_022-default-telemetry-dashboard/` (`.specify/feature.json` is git-ignored, so it stays local) as `docs(specs): add 022 default telemetry dashboard spec`. +- [X] T001 Create branch `feat/default-telemetry-dashboard` from `master` and commit `specs/022-default-telemetry-dashboard/` (`.specify/feature.json` is git-ignored, so it stays local) as `docs(specs): add 022 default telemetry dashboard spec`. - [X] T002 Get **explicit human sign-off** (system override 1) to rewrite these existing tests so they assert the new behaviour. Rewrites must keep or raise coverage and never delete a case without a replacement: - `api/internal/telemetry/telemetry_test.go`: `TestReportOnce_EnabledPostsAnonymousCounts`, `TestReportOnce_SendsAuthHeader`, `TestReportOnce_DisabledSkipsPost`. The reporter's constructor and send path change (research R12). - `api/internal/telemetry/telemetry_branches_test.go`: `TestNew_DefaultsInterval`, `TestEnabled_Branches`, `TestCount_UnknownKind`, `TestReportOnce_EndpointErrorPropagates`. @@ -48,10 +48,10 @@ description: "Task list for feature 022: default telemetry destination, extended - `api/internal/handlers/config_validators_test.go` `TestValidateTelemetry` (added 2026-10-07): the canonical output gains `"extended":false`, and a new case checks that `{"sendMetrics":false,"extended":true}` normalises to both false. **Signed off 2026-10-06** by the user ("Approve all", then "Approve" for the `/metrics` changes; "Assert the seeded default" for `TestConfig_GetEmpty` and "Assert the new shape" for `TestValidateTelemetry` on 2026-10-07). Record the sign-off in the PR description. -- [X] T003 Create `telemetryschema/go.mod` (`module github.com/GameplanePanel/gameplane/telemetryschema`, `go 1.26.0`, no requires) and `telemetryschema/doc.go` with a package comment: "the shared telemetry report contract used by api and telemetry-receiver; see specs/done_022-default-telemetry-dashboard/contracts/report-schema.md". +- [X] T003 Create `telemetryschema/go.mod` (`module github.com/GameplanePanel/gameplane/telemetryschema`, `go 1.26.0`, no requires) and `telemetryschema/doc.go` with a package comment: "the shared telemetry report contract used by api and telemetry-receiver; see specs/022-default-telemetry-dashboard/contracts/report-schema.md". - [X] T004 Add `./telemetryschema` to the `use` block in `go.work`, and add `telemetryschema` to `GO_MODULES` in `Makefile:43` (after `gameproto`). - [X] T005 [P] Add `telemetryschema` to both Go module matrices in `.github/workflows/ci.yaml` (the `module:` lists at about lines 457 and 549). Add `telemetryschema/**` everywhere `telemetry-receiver/**` appears as a path filter (about line 119), and to the api and telemetry-receiver image path filters in `.github/workflows/publish-edge.yaml`. Add a `gomod` entry for `/telemetryschema` in `.github/dependabot.yml`, copied from the `/telemetry-receiver` entry at about line 176. -- [X] T006 [P] Create `telemetryschema/.testcoverage.yml` (copy the `telemetry-receiver/.testcoverage.yml` format with `total: 90` and an updated header comment) and a non-empty `telemetryschema/specs.md` skeleton with the sections Purpose, Responsibilities, External interface (linking `specs/done_022-default-telemetry-dashboard/contracts/report-schema.md`), Key invariants, and Testing & coverage, so that `hack/check-specs.sh` passes. +- [X] T006 [P] Create `telemetryschema/.testcoverage.yml` (copy the `telemetry-receiver/.testcoverage.yml` format with `total: 90` and an updated header comment) and a non-empty `telemetryschema/specs.md` skeleton with the sections Purpose, Responsibilities, External interface (linking `specs/022-default-telemetry-dashboard/contracts/report-schema.md`), Key invariants, and Testing & coverage, so that `hack/check-specs.sh` passes. - [X] T007 [P] Update the repository docs maps: - `CLAUDE.md`: in the repository map add `telemetryschema/ shared telemetry report contract (api, telemetry-receiver)`; change "links all 15 Go modules" to 16; add `telemetryschema 90` to the coverage minimums; add a row to the Architecture table. - `docs/agent-architecture.md`: add a `telemetryschema/specs.md` row next to Telemetry-Receiver (line 29). @@ -566,7 +566,7 @@ description: "Task list for feature 022: default telemetry destination, extended - the OD-1 merge gate - the T002 sign-off list - that `telemetry-default-gate` is expected to fail until OD-1 is ruled -- [X] T099 Once OD-1 is ruled: set `DefaultEndpoint` in `api/internal/telemetry/destination.go`, mark OD-1 RULED in `specs/done_022-default-telemetry-dashboard/OPEN-DECISIONS.md`, update the destination copy in `design.pen` (with re-export) and the docs, confirm `telemetry-default-gate` passes, and mark the PR ready for review. +- [X] T099 Once OD-1 is ruled: set `DefaultEndpoint` in `api/internal/telemetry/destination.go`, mark OD-1 RULED in `specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md`, update the destination copy in `design.pen` (with re-export) and the docs, confirm `telemetry-default-gate` passes, and mark the PR ready for review. The docs written under T091-T094 name no receiver host. Once OD-1 is ruled, add the host to: - `docs/install.md` § Telemetry: the sentence on an empty `api.telemetry.endpoint`, and the Upgrading admonition - `docs/architecture.md`: the "API → telemetry-receiver" bullet diff --git a/telemetry-receiver/specs.md b/telemetry-receiver/specs.md index e0ef41821..dadffa51d 100644 --- a/telemetry-receiver/specs.md +++ b/telemetry-receiver/specs.md @@ -58,7 +58,7 @@ telemetry-receiver/ ## External interface / contracts -The full contract is `specs/done_022-default-telemetry-dashboard/contracts/receiver-http.md`; the report format is `contracts/report-schema.md`. +The full contract is `specs/022-default-telemetry-dashboard/contracts/receiver-http.md`; the report format is `contracts/report-schema.md`. ### Public listener (`LISTEN_ADDR`) @@ -180,5 +180,5 @@ Untested (main process wiring): `main()` signal handling and server startup erro - **`telemetryschema/specs.md`** — shared report contract, signing and proof-of-work helpers - **`api/internal/telemetry`** — the API's reporter that POSTs to this endpoint - **`charts/gameplane/`** — Helm chart integration (`api.telemetry.receiver.*`) -- **`specs/done_022-default-telemetry-dashboard/`** — feature spec and contracts +- **`specs/022-default-telemetry-dashboard/`** — feature spec and contracts - **`CLAUDE.md`** — project architecture summary diff --git a/telemetryschema/doc.go b/telemetryschema/doc.go index 777606c39..1aee4814e 100644 --- a/telemetryschema/doc.go +++ b/telemetryschema/doc.go @@ -1,4 +1,4 @@ // Package telemetryschema is the shared telemetry report contract used by api // and telemetry-receiver; see -// specs/done_022-default-telemetry-dashboard/contracts/report-schema.md. +// specs/022-default-telemetry-dashboard/contracts/report-schema.md. package telemetryschema diff --git a/telemetryschema/specs.md b/telemetryschema/specs.md index e6893859a..abdebf81b 100644 --- a/telemetryschema/specs.md +++ b/telemetryschema/specs.md @@ -45,7 +45,7 @@ telemetryschema/ ## External interface / contracts -The wire contract is [`specs/done_022-default-telemetry-dashboard/contracts/report-schema.md`](../specs/done_022-default-telemetry-dashboard/contracts/report-schema.md); the rules below are what this package enforces. +The wire contract is [`specs/022-default-telemetry-dashboard/contracts/report-schema.md`](../specs/022-default-telemetry-dashboard/contracts/report-schema.md); the rules below are what this package enforces. ### Types @@ -110,7 +110,7 @@ JSON tags are the camelCase names in the contract (`installId`, `wakeOnConnect`, ### Proof-of-work -The wire contract is research R21 and [`contracts/receiver-http.md`](../specs/done_022-default-telemetry-dashboard/contracts/receiver-http.md). When a receiver requires proof-of-work, every report carries `Gameplane-Telemetry-PoW: :`. +The wire contract is research R21 and [`contracts/receiver-http.md`](../specs/022-default-telemetry-dashboard/contracts/receiver-http.md). When a receiver requires proof-of-work, every report carries `Gameplane-Telemetry-PoW: :`. | Name | Behavior | |---|---| From 6a46f5a3106bd5a15dfd0dfab565f69fee37a774 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 00:08:22 +0000 Subject: [PATCH 2/3] docs(specs): amend spec 022 with a per-install version trend Add FR-042, US5 scenarios 7-9 and SC-017: a daily trend of installs that upgraded, downgraded, changed version without an order, or stayed behind the newest release, from extended reports only. Rename the basic panel to "Reports by version". Record OD-7 to OD-9 as ruled and OD-10 as open, research R22, the two new receiver tables, the plan slice and tasks T101-T110. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- .../OPEN-DECISIONS.md | 60 ++++++++++++++++++- .../contracts/receiver-http.md | 1 + .../data-model.md | 4 ++ specs/022-default-telemetry-dashboard/plan.md | 11 ++++ .../research.md | 15 +++++ specs/022-default-telemetry-dashboard/spec.md | 31 +++++++++- .../022-default-telemetry-dashboard/tasks.md | 27 +++++++++ 7 files changed, 146 insertions(+), 3 deletions(-) diff --git a/specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md b/specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md index d4ea95b08..5b529f813 100644 --- a/specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md +++ b/specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md @@ -1,6 +1,6 @@ # Open Decisions -**Status**: 0 open; 6 ruled (OD-2, OD-3 and OD-4 on 2026-10-06, OD-1 and OD-5 on 2026-10-07, OD-6 on 2026-10-08). +**Status**: 1 open (OD-10); 9 ruled (OD-2, OD-3 and OD-4 on 2026-10-06, OD-1 and OD-5 on 2026-10-07, OD-6 on 2026-10-08, OD-7, OD-8 and OD-9 on 2026-10-14). Per CLAUDE.md rule 10, an open value MUST NOT be committed as a settled contract in code, chart defaults or docs until it is ruled here. @@ -110,3 +110,61 @@ Per CLAUDE.md rule 10, an open value MUST NOT be committed as a settled contract **Question**: The receiver dashboard's ranges ended on the latest complete UTC day (yesterday), so a provider saw "No reports yet" for up to a day after its first accepted report, and today's reports never showed until the day closed. **Ruling**: the dashboard shows all data. Every range (7, 30, 90 or 365 days) ends on today, the current UTC day, which is still in progress and labelled so. The empty state appears only when the range holds no report at all. Today's lapsed-installs value stays `0` until the lifecycle job finalises the day. The public `GET /v1/summary` is unchanged and keeps the latest complete day. + +--- + +## Amendment 2026-10-14: version trend (FR-042) + +### OD-7: What "stale" means + +**Status**: RULED (2026-10-14, user) + +**Question**: The version trend counts installs that upgraded, downgraded or went stale. Upgrades and downgrades are detected when an install reports a different version than last time. What makes an install stale? The receiver doesn't know which release is newest today, and installs that stop reporting are already counted as lapsed. + +**Options**: +1. Behind the newest seen: on a day, an install's version is lower than the newest release any install reported in the range. +2. Unchanged for N days. +3. Both: behind the newest seen release for at least N days. + +**Ruling**: (1) behind the newest seen. No threshold to tune. Whether a pre-release can be the newest release is OD-10. + +### OD-8: Ordering versions that aren't releases + +**Status**: RULED (2026-10-14, user) + +**Question**: Hand builds (`pr617`), PR images (`dev`) and edge images (`edge-`) have no order. How does the trend treat them? + +**Options**: +1. Semver only: only semver versions are ordered; a change to or from anything else is "changed (unordered)". +2. Semver, with everything else sorting below every release. +3. Leave installs on non-release builds out of the trend. + +**Ruling**: (1) semver only. The trend never guesses a direction, and an install on a non-semver build is never stale. + +### OD-9: Where the views go + +**Status**: RULED (2026-10-14, user) + +**Question**: The basic "Versions" panel counts reports by version (FR-024), and "Versions by install" in the extended section already counts each install once on its latest version (FR-025). Where do these and the new trend go? + +**Options**: +1. Replace the top panel with the per-install view and the trend. +2. Keep both, rename the reports panel "Reports by version", and put the trend under "Versions by install". +3. Move "Versions by install" and the trend to the top, and the reports panel down. + +**Ruling**: (2) keep both and add the trend. The layout is designed first in `telemetry-receiver/telemetry-dashboard.pen`. + +### OD-10: Pre-releases and the newest-release bar + +**Status**: OPEN + +**Question**: Can a pre-release such as `v0.4.0-rc.1` be the "newest release" that makes other installs stale (OD-7)? Release images report their git tag (`v0.3.0`), and the repo has pre-release tags (`v0.3.0-rc.1`). Semver orders `v0.4.0-rc.1` above `v0.3.0`, so one rc tester would make every install on the newest final release stale. Moving to or from a pre-release still counts as an upgrade or downgrade either way. + +**Options**: +1. Finals only: only versions without a pre-release suffix set the bar. +2. Any semver: the highest semver seen sets the bar, pre-releases included. + +**Recommended default**: (1) finals only. Being on the newest final release isn't stale, and rc testers are few. + +**Blocks**: the stale computation in T106 and its tests in T109. + diff --git a/specs/022-default-telemetry-dashboard/contracts/receiver-http.md b/specs/022-default-telemetry-dashboard/contracts/receiver-http.md index 00e3a4691..c9c477094 100644 --- a/specs/022-default-telemetry-dashboard/contracts/receiver-http.md +++ b/specs/022-default-telemetry-dashboard/contracts/receiver-http.md @@ -116,6 +116,7 @@ An E2E check (R17) compares the unauthenticated response against the login page "newPerDay": [{"day": "2026-09-06", "count": 12}], "lapsedPerDay": [{"day": "2026-09-06", "count": 4}], "versionsByInstall": {"windowDays": 30, "items": [{"label": "0.3.0", "installs": 1011}]}, + "versionTrend": {"newestRelease": "v0.3.0", "days": [{"day": "2026-09-06", "upgraded": 14, "downgraded": 1, "unordered": 0, "stale": 212}]}, "env": {"k8s": [{"label": "1.31", "share": 0.42}], "distro": [], "arch": [], "nodes": []}, "games": [{"module": "minecraft-java", "installsShare": 0.63, "serversPerDay": 2140.5}, {"module": "custom", "installsShare": 0.08, "serversPerDay": 77.1}], "features": [{"feature": "wakeOnConnect", "share": 0.18}], diff --git a/specs/022-default-telemetry-dashboard/data-model.md b/specs/022-default-telemetry-dashboard/data-model.md index 4947f4c9b..f00b291a0 100644 --- a/specs/022-default-telemetry-dashboard/data-model.md +++ b/specs/022-default-telemetry-dashboard/data-model.md @@ -195,6 +195,8 @@ Responses inside one attempt: | `daily_ext` | `day` | `ext_reports`, `active_installs`, `new_installs`, `lapsed_installs` | Ingest for the first three. Rollover for `lapsed_installs`. | | `daily_dim` | `(day, dim, value)` | `installs` | Ingest of a non-duplicate extended report. See the dimension list below. | | `daily_game` | `(day, module)` | `installs`, `servers` | Ingest. `module` is a catalog name or `custom`. | +| `daily_install_version` | `(day, version)` | `installs` | Ingest of a non-duplicate extended report (FR-042). `version` is the same bounded label as `daily_version`. | +| `daily_version_change` | `(day, kind)` | `installs` | Ingest of a non-duplicate extended report whose version differs from `activity.last_version` (FR-042). `kind` is `upgraded`, `downgraded` or `unordered` (R22). | | `activity` | `id_hmac` | `key_fp` (SHA-256 of the claiming public key), `first_seen`, `last_seen`, `last_sent_at`, `last_version` | Ingest: inserting a row *is* the claim (R20). Deleted by expiry, and the claim expires with it (FR-038). | `daily_dim` dimensions: @@ -213,6 +215,7 @@ Every `value` is a `telemetryschema` enumeration member or `other`, so the row c - **Extended attributes are never keyed by ID.** `activity` has no column for any of them (FR-014). - **Refused reports leave no trace.** A report refused by the proof-of-work, signature, window, claim or replay checks changes no table (FR-036, FR-039). Only the in-memory limiter and the `rate_limited` and `refused` metrics see it. - **No full public keys are stored.** `key_fp` is enough to check the claim, because the key arrives with every report. +- **Version changes are counts only.** `daily_version_change` and `daily_install_version` hold no ID, so no version history can be traced back to an install (FR-014, FR-042). - **One extended contribution per install per day.** At most one extended report per `id_hmac` per day changes any aggregate (R5). - **The running total never decreases.** `reports_total` is only ever incremented, so retention sweeps don't change the public "total since collection began". @@ -235,6 +238,7 @@ The range R is 7, 30, 90 or 365 days and ends on today, the current UTC day, whi | Unique active installs | `count(activity where last_seen ≥ asOf − {0, 6, 29})` | | New and lapsed installs | `daily_ext.new_installs` and `lapsed_installs` series over R | | Version adoption (installs) | `activity.last_version` for records with `last_seen` inside `min(R, ACTIVITY_EXPIRY_DAYS)`. The window used is shown. | +| Version trend (FR-042) | Per day D in R: `upgraded`, `downgraded` and `unordered` from `daily_version_change[D]`; `stale[D]` = Σ `daily_install_version[D].installs` over semver versions lower than N, where N is the newest release in `daily_install_version` over R (OD-7; which versions can be N is OD-10). Versions that aren't semver are never stale (OD-8). | | Environment, games, feature shares | Σ `installs` ÷ Σ `daily_ext.ext_reports` over R (install-day weighted, research R4) | | Extended coverage (FR-026) | Σ `ext_reports` ÷ Σ `reports` over R | | Public summary (FR-029) | `reports` for asOf; Σ `reports` over the 30 days ending asOf; `meta.reports_total`; `count(activity where last_seen ≥ asOf − 29)`; `asOf` | diff --git a/specs/022-default-telemetry-dashboard/plan.md b/specs/022-default-telemetry-dashboard/plan.md index 1d1984f45..7f429e7ee 100644 --- a/specs/022-default-telemetry-dashboard/plan.md +++ b/specs/022-default-telemetry-dashboard/plan.md @@ -218,6 +218,17 @@ The slices are implementation waves on **one feature branch and one PR**, in thi Slices 2 and 3 can run in parallel once slice 1 is committed. Slice 4 needs slice 3's routes. Slice 5 needs everything before it. +### Amendment slice (2026-10-14): version trend (FR-042) + +Spec 022 is reopened for one more slice on a new branch and PR; the shipped feature is unchanged until it lands. + +6. **Version trend** (US5 scenarios 7–9, FR-042, OD-7–OD-10, research R22): + - receiver storage: `daily_install_version` and `daily_version_change` (created with `CREATE TABLE IF NOT EXISTS`, so existing providers gain them on restart; no schema-version bump), plus their retention sweep + - version ordering through `golang.org/x/mod/semver`, a new direct dependency of `telemetry-receiver` + - classification in the extended ingest transaction, and the `extended.versionTrend` view block + - the design in `telemetry-receiver/telemetry-dashboard.pen` first (rename "Versions" to "Reports by version", add the trend chart under "Versions by install"), done on the local machine where Pencil runs, then the templates + - the stale computation waits for OD-10 + ## Cross-spec dependencies - **019 localization-language-packs** (Draft) owns adding languages to the `telemetryschema` `language` enumeration and wiring the configured default. Until then `language` is always `en` (R19). diff --git a/specs/022-default-telemetry-dashboard/research.md b/specs/022-default-telemetry-dashboard/research.md index 567bed29f..17f6558b3 100644 --- a/specs/022-default-telemetry-dashboard/research.md +++ b/specs/022-default-telemetry-dashboard/research.md @@ -471,3 +471,18 @@ No new RBAC is needed; every source is already readable (see the baseline facts) - *A memory-hard function such as Argon2.* It resists GPUs better, but `telemetryschema` must stay stdlib-only, and a Raspberry Pi install would pay far more. - *Proof-of-work on the dashboard login.* It needs JavaScript, which the dashboard's CSP forbids (R7), and the 32-character credential (R8) already makes guessing infeasible. - *A fixed difficulty.* Either every install pays all the time, or the difficulty is too low to matter under attack. + +## R22. Version trend: ordering and storage (FR-042, OD-7–OD-10) + +**Decision**: +- **Ordering** uses `golang.org/x/mod/semver`, already used in the workspace (`operator/internal/controller/semver.go`, `operator/internal/oci`) at v0.41.0. A label without a leading `v` is compared as `v` + label, so `0.3.0` and `v0.3.0` order alike. A label `semver.IsValid` rejects (`pr617`, `dev`, `edge-`, `other`, `invalid`) is unordered (OD-8). +- **Classification at ingest**, on a non-duplicate extended report for an existing activity record whose `last_version` differs from the new label: both semver and new > old is `upgraded`, new < old is `downgraded`, otherwise `unordered`. Labels that differ but compare equal under semver (`0.3.0` and `v0.3.0`) are not a change. The write shares the transaction that updates the activity record, so a refused or duplicate report changes nothing. +- **Stale at read time** from `daily_install_version`, so the "newest release in the range" (OD-7) follows the selected range instead of being frozen at ingest time. + +**Rationale**: Daily counts keep FR-014 (no attribute linked to an ID) while still giving a per-day trend. Storing installs per version per day costs at most the bounded label set (`maxVersionLabels` = 128, plus `other` and `invalid`) per day. + +**Alternatives considered**: +- A per-install version history table: rejected, it would link versions to an ID over time (FR-014). +- Stale at ingest against the newest version seen so far: rejected, the result would depend on arrival order and ignore the selected range. +- A hand-written semver parser in `telemetryschema`: rejected, the `x/mod` package is the reference implementation and is already vetted in this workspace. + diff --git a/specs/022-default-telemetry-dashboard/spec.md b/specs/022-default-telemetry-dashboard/spec.md index 73baa628d..8f1eba606 100644 --- a/specs/022-default-telemetry-dashboard/spec.md +++ b/specs/022-default-telemetry-dashboard/spec.md @@ -4,7 +4,7 @@ **Created**: 2026-10-06 -**Status**: Draft (clarified) +**Status**: Draft (clarified); amended 2026-10-14 with the version trend (US5, FR-042) **Input**: User description: "Right now telemetry is only something someone can add. i want to add a default to a domain (not yet defined) and also build a dashboard to show the data provided by the telemetry provider". Follow-ups: "also could we extend the telemetry?" and "right now there is no verif for data sent. so i want a way for server to 'claim' the id with a key derivated from stable local value to sign the data so no one can impersonatte other id" @@ -32,6 +32,15 @@ - Q11: Per-source limits can be outrun by anyone holding many IPv6 /64s. What protects the dashboard credential? → A: **The provider refuses to start with a dashboard credential shorter than 32 characters**, so guessing it stays infeasible however many sources an attacker controls. Per-source limits remain a cost, not a bound (security review of 0819cec1). - Q12: Should sending a report cost the sender work, as Anubis does for browsers? → A: **Yes, for report ingestion only (not the dashboard login), as an Anubis-style proof-of-work.** It is off by default, including in the bundled receiver, and the project's provider turns it on through the runbook. When it is on, the difficulty follows the request rate: none while traffic is normal or absent, rising fast as requests flood in, and falling slowly once they stop. Default values are ruled in OD-5. +### Session 2026-10-14 (amendment: version trend) + +The Versions panel showed "pr617 6" for one install that had sent 6 daily reports, which reads like 6 installs. The user asked for the current version per install, with a trend of installs that upgrade, go stale or downgrade over time, and chose to amend this spec rather than start a new one. + +- Q13: What does "stale" mean? → A: **Behind the newest release seen.** On a given day, an install is stale when its version is lower than the newest release any install reported in the selected range (OD-7). +- Q14: How are versions that aren't releases (`pr617`, `dev`, `edge-`) ordered? → A: **Semver only.** Only semver versions are ordered. A change to or from anything else counts as "changed (unordered)", never as an upgrade or downgrade, and such an install is never stale (OD-8). +- Q15: Where do the views go? → A: **Keep both, add the trend.** The basic reports-by-version panel stays and is renamed "Reports by version". The trend goes under "Versions by install" in the extended section (OD-9). +- Q16: Can a pre-release (`v0.4.0-rc.1`) set the newest-release bar for stale? → OPEN (OD-10). + ## Context Today an install sends anonymous usage reports (`{version, servers, templates}`, about once a day) only if **two** gates are both open: @@ -172,6 +181,9 @@ These answer questions that basic counts can't, such as "how many installs are s 4. **Given** extended reports, **When** an authorized viewer looks at feature adoption, **Then** it shows the share of installs using each tracked feature. 5. **Given** some installs share only basic data, **When** an authorized viewer looks at any extended figure, **Then** the dashboard states how many of the reports in the range included extended data, so coverage is clear. 6. **Given** an install that upgraded within the range, **When** an authorized viewer looks at version adoption by install, **Then** the install is counted on its latest reported version. +7. **Given** installs whose extended reports change version within the range, **When** an authorized viewer looks at the version trend, **Then** it shows per day how many installs upgraded, downgraded, or changed between versions that can't be ordered (FR-042). +8. **Given** installs active on several semver versions within the range, **When** an authorized viewer looks at the version trend, **Then** it shows per day how many active installs were stale, that is, on a version lower than the newest release seen in the range (FR-042, OD-7, OD-10). +9. **Given** an install that only ever reports a version that isn't semver (for example `pr617`), **When** an authorized viewer looks at the version trend, **Then** that install is never counted as upgraded, downgraded or stale, and a change of its version counts only as "changed (unordered)". --- @@ -320,6 +332,8 @@ Someone learns an install's ID, for example from a screenshot of Admin Settings, - installs and servers per official module, plus "custom" - per-feature adoption counts - the number of reports that included extended data + - active installs per reported version + - installs that upgraded, downgraded, or changed version without an order (FR-042) - **FR-021**: The provider MUST limit how many reports a single source can submit per day, so that one source can't distort the figures by flooding. **Private dashboard** @@ -328,7 +342,7 @@ Someone learns an install's ID, for example from a screenshot of Admin Settings, - **FR-023**: Unauthenticated access to the dashboard or its data MUST be refused with a generic message that reveals no figures, dates, categories, or whether any data exists. - **FR-024**: The dashboard MUST show these **basic** views: - reports per day as a trend - - version adoption, with the top 10 versions plus "Other" and "Invalid" + - version adoption by reports, titled "Reports by version", with the top 10 versions plus "Other" and "Invalid" (OD-9) - fleet-size distributions and medians for servers and templates - total servers and templates today (in progress) - a "data as of" timestamp @@ -336,6 +350,7 @@ Someone learns an install's ID, for example from a screenshot of Admin Settings, - unique active installs (today in progress, trailing 7 and 30 days) - new and lapsed installs per period - version adoption by install + - the version trend (FR-042), placed under version adoption by install (OD-9) - environment breakdowns - game usage by installs and by servers, including "custom" - feature adoption shares @@ -377,6 +392,16 @@ Someone learns an install's ID, for example from a screenshot of Admin Settings, - **FR-040**: When its provider offers challenges, the install MUST obtain and solve one before each report. When the provider doesn't offer them, the install MUST send without proof-of-work, so older and self-hosted providers keep working. The install MUST refuse to solve a challenge harder than a fixed cap (OD-5) and record the attempt as failed. When the provider refuses a report for its proof-of-work, the install MUST obtain a new challenge and resend once in the same attempt. Solving MUST NOT block the API's request handling. - **FR-041**: The provider MUST refuse to start when a dashboard credential is set but shorter than 32 characters. +**Version trend** (amendment 2026-10-14) + +- **FR-042**: The provider MUST derive a daily version trend from extended reports only, because basic reports carry no install ID. For each day in the range it shows: + - **upgraded**: installs whose first non-duplicate report that day carries a semver version higher than their activity record's last version + - **downgraded**: the same, with a lower semver version + - **changed (unordered)**: installs whose version changed where either side isn't semver (OD-8) + - **stale**: installs active that day on a semver version lower than the newest release reported by any install in the range (OD-7). Whether a pre-release can be that newest release is OD-10. Installs on a version that isn't semver are never stale. + + An install's first report (no activity record yet) is never a change. Version changes MUST be stored only as daily counts, never linked to an install ID (FR-014). The trend starts on the day the provider gains this feature, with no backfill. + ### Key Entities - **Basic report**: one anonymous sample from one install, carrying version, server count, and template count. Unchanged by this feature. @@ -388,6 +413,7 @@ Someone learns an install's ID, for example from a screenshot of Admin Settings, - **Telemetry notice acknowledgement**: per admin, whether the FR-004 notice has been shown and dismissed. It gates the first report on new installs. - **Delivery status**: per install, the last attempt time, the last successful report time, and the last outcome. Shown to admins and never sent anywhere. - **Daily aggregate**: per calendar day (UTC) on the provider. Holds the basic and extended tallies listed in FR-020. It's the only form in which report contents are kept. +- **Version change**: a daily count of installs whose reported version went up, went down, or changed without an order (FR-042). Never linked to an ID. - **Activity record**: per install ID on the provider. Holds the transformed ID, the fingerprint of the key that claimed it, the first-seen and last-seen dates, the latest accepted send time, and the last version. It expires after inactivity (OD-4), and the claim expires with it. - **Dashboard access credential**: the provider operator's secret that unlocks the private dashboard. It can be replaced without touching stored data. - **Public summary**: a view derived from daily aggregates and activity records, limited to the values listed in FR-029. @@ -421,6 +447,7 @@ Someone learns an install's ID, for example from a screenshot of Admin Settings, - **SC-014**: In a test of every forgery case in US7 scenarios 1–3 against a claimed ID, 100% of forged reports are refused and 0 figures change. - **SC-015**: An install whose ID was claimed by another key has a report accepted under a new ID within the same delivery attempt in 100% of tested cases. - **SC-016**: With proof-of-work on, a challenge issued while demand is at or below the normal rate requires no work, and a challenge issued while demand is 1,000 times the normal rate requires at least 2^20 hash attempts on average (FR-039). +- **SC-017**: For a synthetic population with known upgrades, downgrades, unordered changes and installs left on older releases, every daily upgraded, downgraded, unordered and stale count on the version trend matches the expected value exactly. ## Assumptions diff --git a/specs/022-default-telemetry-dashboard/tasks.md b/specs/022-default-telemetry-dashboard/tasks.md index 790cc08d9..d5690daad 100644 --- a/specs/022-default-telemetry-dashboard/tasks.md +++ b/specs/022-default-telemetry-dashboard/tasks.md @@ -577,6 +577,32 @@ description: "Task list for feature 022: default telemetry destination, extended --- +## Phase 11: Amendment 2026-10-14 — version trend (US5, FR-042) + +**Goal**: The dashboard shows, per day, how many installs upgraded, downgraded, changed version without an order, or stayed behind the newest release (OD-7–OD-10, research R22). The basic version panel is renamed "Reports by version" (OD-9). + +**Independent Test**: A synthetic population with known upgrades, downgrades, unordered changes (`pr617` → `v0.3.0`) and installs left on older releases gives exact daily counts for every series (SC-017). + +**Branch and PR**: a new branch from `master` and a new PR; the merged 022 PR (#617) is not reused. + +- [ ] T101 [US5] In `telemetry-receiver/store.go`, add `daily_install_version (day, version, installs)` and `daily_version_change (day, kind, installs)` to the `CREATE TABLE IF NOT EXISTS` list (data-model § Receiver), and add both to the retention `DELETE FROM … WHERE day < ?` list near line 293. +- [ ] T102 [P] [US5] Create `telemetry-receiver/versionorder.go`: `classifyChange(old, new string) (kind string, changed bool)` and `isRelease(label string) bool`, using `golang.org/x/mod/semver` (v0.41.0, as in `operator/go.mod`) with a `v` prefix added to labels that lack one (R22). Add the dependency to `telemetry-receiver/go.mod` with `make tidy`; confirm with `go build ./...`. +- [ ] T103 [US5] In `telemetry-receiver/ingest_ext.go`, inside the transaction of a non-duplicate extended report (the insert near line 197 and the update near line 225): increment `daily_install_version[today, label]`; when the record existed and `classifyChange(last_version, label)` reports a change, increment `daily_version_change[today, kind]`. A new record is never a change (FR-042). Duplicates and refused reports write nothing. +- [ ] T104 [US5] Design first, on the local machine where Pencil runs: in `telemetry-receiver/telemetry-dashboard.pen`, rename the basic "Versions" card to "Reports by version" and add a "Version trend" card under "Versions by install" in the extended section, with four daily series (upgraded, downgraded, changed (unordered), stale) and the newest release named in the caption. Load the `dataviz` skill for the chart form and colours. Ask the user to save in the Pencil UI, run `design-export` into `telemetry-receiver/design-export/`, and commit the export with the design (rules 1, 2). +- [ ] T105 [US5] In `telemetry-receiver/views.go`, add `extended.versionTrend` (`contracts/receiver-http.md`): one entry per day in R with `upgraded`, `downgraded` and `unordered` from `daily_version_change`, and `newestRelease`. `null` with the rest of `extended` when the range has no extended reports. +- [ ] T106 [US5] Blocked on OD-10. In `telemetry-receiver/views.go`, compute `stale` per day and `newestRelease` from `daily_install_version` over R (data-model § Derived views): N is the highest version in R that `isRelease` accepts under the OD-10 ruling; `stale[D]` sums installs on semver versions below N. Non-semver versions are never stale (OD-8). +- [ ] T107 [US5] After T104: in `telemetry-receiver/charts.go:350` change the card title to "Reports by version" and update its caption in `web/overview.html:37`; add the trend chart to `charts_ext.go` and `web/overview_extended.html` under "Versions by install", following the T104 design. +- [ ] T108 [P] [US5] Docs: describe the trend, the two tables and the "Reports by version" rename in `telemetry-receiver/specs.md` and `README.md`, and mention the two new daily counts wherever `docs/telemetry-provider.md` or the `website/` data-handling statement describe what the receiver stores (the statement change goes through the submodule's own PR flow). +- [ ] T109 [US5] Tests (CI only, rule 8): + - new `telemetry-receiver/versionorder_test.go`: semver up, down, equal across `v` prefixes, pre-releases, and every non-semver label as unordered + - new cases in `ingest_ext_test.go` and `population_test.go` for SC-017, including duplicates and refused reports leaving both tables unchanged, and `views_test.go` for `versionTrend` with and without extended data + - **needs the user's sign-off before editing** (existing tests whose expectations change): `lifecycle_test.go:11` adds the two tables to `dailyTables` so the retention sweep covers them; `charts_test.go:83` if it asserts the old "Versions" title +- [ ] T110 Push the branch and open the PR with the `ship-branch` skill; labels `type: feature`, `area: shared`, `area: specs` through REST (rule 14). After merge, `git mv specs/022-default-telemetry-dashboard specs/done_022-default-telemetry-dashboard`, update in-repo references, and delete the branch (rules 12, 16). + +**Checkpoint**: the trend is visible and every series matches SC-017. + +--- + ## Dependencies and execution order ### Phase dependencies @@ -605,6 +631,7 @@ description: "Task list for feature 022: default telemetry destination, extended - T108 depends on T102, T105 and T107 - T109 depends on T102 and T103 - **Polish (Phase 10)** depends on every story. T099 also waits for OD-1, and T100 waits for the merge. +- **Amendment (Phase 11)** depends only on the shipped feature. T101–T103 and T105 can start at once; T104 runs on the local machine; T106 waits for OD-10; T107 waits for T104; T109 waits for T101–T107 and the sign-off it names. ### Story order (graph) From 246176e86e2c9a4d7d73b9e6928d4bac3c2af873 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 00:20:27 +0000 Subject: [PATCH 3/3] docs(specs): rule OD-10, pre-releases set the stale bar valgul ruled any semver: the highest semver version seen in the range, pre-releases included, is the newest release for the stale count. Co-Authored-By: Claude Opus 5.5 (1M context) Signed-off-by: Claude --- specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md | 8 +++----- specs/022-default-telemetry-dashboard/data-model.md | 2 +- specs/022-default-telemetry-dashboard/plan.md | 1 - specs/022-default-telemetry-dashboard/spec.md | 4 ++-- specs/022-default-telemetry-dashboard/tasks.md | 6 +++--- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md b/specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md index 5b529f813..0462256a5 100644 --- a/specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md +++ b/specs/022-default-telemetry-dashboard/OPEN-DECISIONS.md @@ -1,6 +1,6 @@ # Open Decisions -**Status**: 1 open (OD-10); 9 ruled (OD-2, OD-3 and OD-4 on 2026-10-06, OD-1 and OD-5 on 2026-10-07, OD-6 on 2026-10-08, OD-7, OD-8 and OD-9 on 2026-10-14). +**Status**: 0 open; 10 ruled (OD-2, OD-3 and OD-4 on 2026-10-06, OD-1 and OD-5 on 2026-10-07, OD-6 on 2026-10-08, OD-7 to OD-10 on 2026-10-14). Per CLAUDE.md rule 10, an open value MUST NOT be committed as a settled contract in code, chart defaults or docs until it is ruled here. @@ -156,7 +156,7 @@ Per CLAUDE.md rule 10, an open value MUST NOT be committed as a settled contract ### OD-10: Pre-releases and the newest-release bar -**Status**: OPEN +**Status**: RULED (2026-10-14, user) **Question**: Can a pre-release such as `v0.4.0-rc.1` be the "newest release" that makes other installs stale (OD-7)? Release images report their git tag (`v0.3.0`), and the repo has pre-release tags (`v0.3.0-rc.1`). Semver orders `v0.4.0-rc.1` above `v0.3.0`, so one rc tester would make every install on the newest final release stale. Moving to or from a pre-release still counts as an upgrade or downgrade either way. @@ -164,7 +164,5 @@ Per CLAUDE.md rule 10, an open value MUST NOT be committed as a settled contract 1. Finals only: only versions without a pre-release suffix set the bar. 2. Any semver: the highest semver seen sets the bar, pre-releases included. -**Recommended default**: (1) finals only. Being on the newest final release isn't stale, and rc testers are few. - -**Blocks**: the stale computation in T106 and its tests in T109. +**Ruling**: (2) any semver. The highest semver version seen in the range sets the bar, pre-releases included. One install on `v0.4.0-rc.1` makes every install on `v0.3.0` stale for the days in the range. diff --git a/specs/022-default-telemetry-dashboard/data-model.md b/specs/022-default-telemetry-dashboard/data-model.md index f00b291a0..a0e6e4f22 100644 --- a/specs/022-default-telemetry-dashboard/data-model.md +++ b/specs/022-default-telemetry-dashboard/data-model.md @@ -238,7 +238,7 @@ The range R is 7, 30, 90 or 365 days and ends on today, the current UTC day, whi | Unique active installs | `count(activity where last_seen ≥ asOf − {0, 6, 29})` | | New and lapsed installs | `daily_ext.new_installs` and `lapsed_installs` series over R | | Version adoption (installs) | `activity.last_version` for records with `last_seen` inside `min(R, ACTIVITY_EXPIRY_DAYS)`. The window used is shown. | -| Version trend (FR-042) | Per day D in R: `upgraded`, `downgraded` and `unordered` from `daily_version_change[D]`; `stale[D]` = Σ `daily_install_version[D].installs` over semver versions lower than N, where N is the newest release in `daily_install_version` over R (OD-7; which versions can be N is OD-10). Versions that aren't semver are never stale (OD-8). | +| Version trend (FR-042) | Per day D in R: `upgraded`, `downgraded` and `unordered` from `daily_version_change[D]`; `stale[D]` = Σ `daily_install_version[D].installs` over semver versions lower than N, where N is the newest release in `daily_install_version` over R (OD-7): the highest semver version, pre-releases included (OD-10). Versions that aren't semver are never stale (OD-8). | | Environment, games, feature shares | Σ `installs` ÷ Σ `daily_ext.ext_reports` over R (install-day weighted, research R4) | | Extended coverage (FR-026) | Σ `ext_reports` ÷ Σ `reports` over R | | Public summary (FR-029) | `reports` for asOf; Σ `reports` over the 30 days ending asOf; `meta.reports_total`; `count(activity where last_seen ≥ asOf − 29)`; `asOf` | diff --git a/specs/022-default-telemetry-dashboard/plan.md b/specs/022-default-telemetry-dashboard/plan.md index 7f429e7ee..43d8dd23c 100644 --- a/specs/022-default-telemetry-dashboard/plan.md +++ b/specs/022-default-telemetry-dashboard/plan.md @@ -227,7 +227,6 @@ Spec 022 is reopened for one more slice on a new branch and PR; the shipped feat - version ordering through `golang.org/x/mod/semver`, a new direct dependency of `telemetry-receiver` - classification in the extended ingest transaction, and the `extended.versionTrend` view block - the design in `telemetry-receiver/telemetry-dashboard.pen` first (rename "Versions" to "Reports by version", add the trend chart under "Versions by install"), done on the local machine where Pencil runs, then the templates - - the stale computation waits for OD-10 ## Cross-spec dependencies diff --git a/specs/022-default-telemetry-dashboard/spec.md b/specs/022-default-telemetry-dashboard/spec.md index 8f1eba606..06ce3c7a0 100644 --- a/specs/022-default-telemetry-dashboard/spec.md +++ b/specs/022-default-telemetry-dashboard/spec.md @@ -39,7 +39,7 @@ The Versions panel showed "pr617 6" for one install that had sent 6 daily report - Q13: What does "stale" mean? → A: **Behind the newest release seen.** On a given day, an install is stale when its version is lower than the newest release any install reported in the selected range (OD-7). - Q14: How are versions that aren't releases (`pr617`, `dev`, `edge-`) ordered? → A: **Semver only.** Only semver versions are ordered. A change to or from anything else counts as "changed (unordered)", never as an upgrade or downgrade, and such an install is never stale (OD-8). - Q15: Where do the views go? → A: **Keep both, add the trend.** The basic reports-by-version panel stays and is renamed "Reports by version". The trend goes under "Versions by install" in the extended section (OD-9). -- Q16: Can a pre-release (`v0.4.0-rc.1`) set the newest-release bar for stale? → OPEN (OD-10). +- Q16: Can a pre-release (`v0.4.0-rc.1`) set the newest-release bar for stale? → A: **Any semver.** The highest semver version seen in the range sets the bar, pre-releases included, so one install on `v0.4.0-rc.1` makes installs on `v0.3.0` stale (OD-10). ## Context @@ -398,7 +398,7 @@ Someone learns an install's ID, for example from a screenshot of Admin Settings, - **upgraded**: installs whose first non-duplicate report that day carries a semver version higher than their activity record's last version - **downgraded**: the same, with a lower semver version - **changed (unordered)**: installs whose version changed where either side isn't semver (OD-8) - - **stale**: installs active that day on a semver version lower than the newest release reported by any install in the range (OD-7). Whether a pre-release can be that newest release is OD-10. Installs on a version that isn't semver are never stale. + - **stale**: installs active that day on a semver version lower than the newest release reported by any install in the range (OD-7). The newest release is the highest semver version seen, pre-releases included (OD-10). Installs on a version that isn't semver are never stale. An install's first report (no activity record yet) is never a change. Version changes MUST be stored only as daily counts, never linked to an install ID (FR-014). The trend starts on the day the provider gains this feature, with no backfill. diff --git a/specs/022-default-telemetry-dashboard/tasks.md b/specs/022-default-telemetry-dashboard/tasks.md index d5690daad..8c61deead 100644 --- a/specs/022-default-telemetry-dashboard/tasks.md +++ b/specs/022-default-telemetry-dashboard/tasks.md @@ -586,11 +586,11 @@ description: "Task list for feature 022: default telemetry destination, extended **Branch and PR**: a new branch from `master` and a new PR; the merged 022 PR (#617) is not reused. - [ ] T101 [US5] In `telemetry-receiver/store.go`, add `daily_install_version (day, version, installs)` and `daily_version_change (day, kind, installs)` to the `CREATE TABLE IF NOT EXISTS` list (data-model § Receiver), and add both to the retention `DELETE FROM … WHERE day < ?` list near line 293. -- [ ] T102 [P] [US5] Create `telemetry-receiver/versionorder.go`: `classifyChange(old, new string) (kind string, changed bool)` and `isRelease(label string) bool`, using `golang.org/x/mod/semver` (v0.41.0, as in `operator/go.mod`) with a `v` prefix added to labels that lack one (R22). Add the dependency to `telemetry-receiver/go.mod` with `make tidy`; confirm with `go build ./...`. +- [ ] T102 [P] [US5] Create `telemetry-receiver/versionorder.go`: `classifyChange(old, new string) (kind string, changed bool)` and `isSemver(label string) bool`, using `golang.org/x/mod/semver` (v0.41.0, as in `operator/go.mod`) with a `v` prefix added to labels that lack one (R22). Add the dependency to `telemetry-receiver/go.mod` with `make tidy`; confirm with `go build ./...`. - [ ] T103 [US5] In `telemetry-receiver/ingest_ext.go`, inside the transaction of a non-duplicate extended report (the insert near line 197 and the update near line 225): increment `daily_install_version[today, label]`; when the record existed and `classifyChange(last_version, label)` reports a change, increment `daily_version_change[today, kind]`. A new record is never a change (FR-042). Duplicates and refused reports write nothing. - [ ] T104 [US5] Design first, on the local machine where Pencil runs: in `telemetry-receiver/telemetry-dashboard.pen`, rename the basic "Versions" card to "Reports by version" and add a "Version trend" card under "Versions by install" in the extended section, with four daily series (upgraded, downgraded, changed (unordered), stale) and the newest release named in the caption. Load the `dataviz` skill for the chart form and colours. Ask the user to save in the Pencil UI, run `design-export` into `telemetry-receiver/design-export/`, and commit the export with the design (rules 1, 2). - [ ] T105 [US5] In `telemetry-receiver/views.go`, add `extended.versionTrend` (`contracts/receiver-http.md`): one entry per day in R with `upgraded`, `downgraded` and `unordered` from `daily_version_change`, and `newestRelease`. `null` with the rest of `extended` when the range has no extended reports. -- [ ] T106 [US5] Blocked on OD-10. In `telemetry-receiver/views.go`, compute `stale` per day and `newestRelease` from `daily_install_version` over R (data-model § Derived views): N is the highest version in R that `isRelease` accepts under the OD-10 ruling; `stale[D]` sums installs on semver versions below N. Non-semver versions are never stale (OD-8). +- [ ] T106 [US5] In `telemetry-receiver/views.go`, compute `stale` per day and `newestRelease` from `daily_install_version` over R (data-model § Derived views): N is the highest semver version in R, pre-releases included (OD-10); `stale[D]` sums installs on semver versions below N. Non-semver versions are never stale (OD-8). - [ ] T107 [US5] After T104: in `telemetry-receiver/charts.go:350` change the card title to "Reports by version" and update its caption in `web/overview.html:37`; add the trend chart to `charts_ext.go` and `web/overview_extended.html` under "Versions by install", following the T104 design. - [ ] T108 [P] [US5] Docs: describe the trend, the two tables and the "Reports by version" rename in `telemetry-receiver/specs.md` and `README.md`, and mention the two new daily counts wherever `docs/telemetry-provider.md` or the `website/` data-handling statement describe what the receiver stores (the statement change goes through the submodule's own PR flow). - [ ] T109 [US5] Tests (CI only, rule 8): @@ -631,7 +631,7 @@ description: "Task list for feature 022: default telemetry destination, extended - T108 depends on T102, T105 and T107 - T109 depends on T102 and T103 - **Polish (Phase 10)** depends on every story. T099 also waits for OD-1, and T100 waits for the merge. -- **Amendment (Phase 11)** depends only on the shipped feature. T101–T103 and T105 can start at once; T104 runs on the local machine; T106 waits for OD-10; T107 waits for T104; T109 waits for T101–T107 and the sign-off it names. +- **Amendment (Phase 11)** depends only on the shipped feature. T101–T103 and T105 can start at once; T104 runs on the local machine; T107 waits for T104; T109 waits for T101–T107 and the sign-off it names. ### Story order (graph)