diff --git a/openspec/CAPABILITIES.md b/openspec/CAPABILITIES.md index 89275fb..734a103 100644 --- a/openspec/CAPABILITIES.md +++ b/openspec/CAPABILITIES.md @@ -68,6 +68,8 @@ When adding a new spec, slot it into the most relevant group below and update th - [`activity-sport-type`](specs/activity-sport-type/spec.md) — sport/activity-type enum on activities (set on create or normalized on import), shown as a badge + feed verb, federated as a Note PropertyValue. - [`activity-stats`](specs/activity-stats/spec.md) — the canonical headline metric set + reusable stat row (sport-aware avg pace/speed, moving-vs-elapsed time) across feed, detail, and profile. - [`journal-elevation-profile`](specs/journal-elevation-profile/spec.md) — read-only elevation profile chart + chart↔map "active distance" hover/click sync on the route & activity detail pages. +- [`profile-stats`](specs/profile-stats/spec.md) — profile roll-up header (lifetime totals + last-4-weeks count), viewer-scoped, computed on the fly (no cache table). +- [`profile-weekly-distance`](specs/profile-weekly-distance/spec.md) — weekly distance bar chart on the profile (12-week axis, gridlines, hover readout), SQL gap-filled. - [`legal-disclaimers`](specs/legal-disclaimers/spec.md) — Terms / Privacy / Imprint pages. - [`transactional-emails`](specs/transactional-emails/spec.md) — magic-link, welcome, etc. email templates. - [`demo-activity-bot`](specs/demo-activity-bot/spec.md) — synthetic-content bot (Bruno by default) seeded for demo instances; configurable persona, retention, and seed region. diff --git a/openspec/changes/profile-stats/.openspec.yaml b/openspec/changes/archive/2026-06-14-profile-stats/.openspec.yaml similarity index 100% rename from openspec/changes/profile-stats/.openspec.yaml rename to openspec/changes/archive/2026-06-14-profile-stats/.openspec.yaml diff --git a/openspec/changes/profile-stats/design.md b/openspec/changes/archive/2026-06-14-profile-stats/design.md similarity index 100% rename from openspec/changes/profile-stats/design.md rename to openspec/changes/archive/2026-06-14-profile-stats/design.md diff --git a/openspec/changes/profile-stats/proposal.md b/openspec/changes/archive/2026-06-14-profile-stats/proposal.md similarity index 100% rename from openspec/changes/profile-stats/proposal.md rename to openspec/changes/archive/2026-06-14-profile-stats/proposal.md diff --git a/openspec/changes/profile-stats/specs/profile-stats/spec.md b/openspec/changes/archive/2026-06-14-profile-stats/specs/profile-stats/spec.md similarity index 100% rename from openspec/changes/profile-stats/specs/profile-stats/spec.md rename to openspec/changes/archive/2026-06-14-profile-stats/specs/profile-stats/spec.md diff --git a/openspec/changes/profile-stats/tasks.md b/openspec/changes/archive/2026-06-14-profile-stats/tasks.md similarity index 100% rename from openspec/changes/profile-stats/tasks.md rename to openspec/changes/archive/2026-06-14-profile-stats/tasks.md diff --git a/openspec/changes/profile-weekly-distance/.openspec.yaml b/openspec/changes/archive/2026-06-14-profile-weekly-distance/.openspec.yaml similarity index 100% rename from openspec/changes/profile-weekly-distance/.openspec.yaml rename to openspec/changes/archive/2026-06-14-profile-weekly-distance/.openspec.yaml diff --git a/openspec/changes/profile-weekly-distance/design.md b/openspec/changes/archive/2026-06-14-profile-weekly-distance/design.md similarity index 100% rename from openspec/changes/profile-weekly-distance/design.md rename to openspec/changes/archive/2026-06-14-profile-weekly-distance/design.md diff --git a/openspec/changes/profile-weekly-distance/proposal.md b/openspec/changes/archive/2026-06-14-profile-weekly-distance/proposal.md similarity index 100% rename from openspec/changes/profile-weekly-distance/proposal.md rename to openspec/changes/archive/2026-06-14-profile-weekly-distance/proposal.md diff --git a/openspec/changes/profile-weekly-distance/specs/profile-weekly-distance/spec.md b/openspec/changes/archive/2026-06-14-profile-weekly-distance/specs/profile-weekly-distance/spec.md similarity index 100% rename from openspec/changes/profile-weekly-distance/specs/profile-weekly-distance/spec.md rename to openspec/changes/archive/2026-06-14-profile-weekly-distance/specs/profile-weekly-distance/spec.md diff --git a/openspec/changes/profile-weekly-distance/tasks.md b/openspec/changes/archive/2026-06-14-profile-weekly-distance/tasks.md similarity index 100% rename from openspec/changes/profile-weekly-distance/tasks.md rename to openspec/changes/archive/2026-06-14-profile-weekly-distance/tasks.md diff --git a/openspec/specs/profile-stats/spec.md b/openspec/specs/profile-stats/spec.md new file mode 100644 index 0000000..04f7d05 --- /dev/null +++ b/openspec/specs/profile-stats/spec.md @@ -0,0 +1,41 @@ +# profile-stats Specification + +## Purpose +TBD - created by archiving change profile-stats. Update Purpose after archive. +## Requirements +### Requirement: Lifetime totals on the profile +The profile page SHALL show a stats header with the owner's lifetime totals: activity count, total distance, total ascent, and total elapsed time. + +#### Scenario: Totals shown for an athlete with activities +- **WHEN** a user views a profile whose owner has activities +- **THEN** a stats header shows the activity count, total distance, total ascent, and total elapsed time + +#### Scenario: Header hidden when there are none +- **WHEN** the profile owner has no activities the viewer may see +- **THEN** no stats header is shown + +### Requirement: Viewer-scoped roll-ups +The roll-up SHALL count only the activities the viewer is permitted to see: public-only for other viewers, and all of the owner's activities when the owner views their own profile. + +#### Scenario: Visitor sees public-only totals +- **WHEN** a visitor (not the owner) views a profile +- **THEN** the totals aggregate only the owner's public activities (unlisted and private are excluded) + +#### Scenario: Owner sees their full totals +- **WHEN** the owner views their own profile +- **THEN** the totals aggregate all of the owner's activities regardless of visibility + +### Requirement: Recent-activity summary +The stats header SHALL show the count of activities in the last 4 weeks (rolling), under the same viewer scoping as the totals. + +#### Scenario: Last-4-weeks count +- **WHEN** the stats header is shown +- **THEN** it includes the number of the owner's (viewer-scoped) activities started in the last 28 days + +### Requirement: Localized stat labels +The stats header SHALL render its labels and units through localized strings. + +#### Scenario: German labels +- **WHEN** the UI locale is German +- **THEN** the stat labels render in German + diff --git a/openspec/specs/profile-weekly-distance/spec.md b/openspec/specs/profile-weekly-distance/spec.md new file mode 100644 index 0000000..0c90dae --- /dev/null +++ b/openspec/specs/profile-weekly-distance/spec.md @@ -0,0 +1,37 @@ +# profile-weekly-distance Specification + +## Purpose +TBD - created by archiving change profile-weekly-distance. Update Purpose after archive. +## Requirements +### Requirement: Weekly distance chart on the profile +The profile page SHALL show a weekly-distance bar chart beneath the stats header, with one bar per week for the last 12 weeks and bar heights normalized to the busiest week in the window. + +#### Scenario: Chart shown when there is recent distance +- **WHEN** a user views a profile whose owner has distance in the last 12 weeks +- **THEN** a 12-week bar chart is shown, the tallest bar being the week with the most distance + +#### Scenario: Chart hidden without recent distance +- **WHEN** the owner has no (viewer-visible) distance in the last 12 weeks +- **THEN** no weekly chart is shown + +### Requirement: Contiguous weekly axis +The chart SHALL render a contiguous run of weeks, including weeks with no activity as zero-height bars, so the axis is stable. + +#### Scenario: Empty weeks appear as gaps +- **WHEN** the owner was active in some weeks of the window but not others +- **THEN** the inactive weeks render as zero-height bars in their correct position (not omitted) + +### Requirement: Viewer-scoped weekly distance +The weekly distance SHALL be scoped to what the viewer may see: public-only for other viewers, and all of the owner's activities when the owner views their own profile. + +#### Scenario: Visitor sees public-only weeks +- **WHEN** a visitor (not the owner) views a profile +- **THEN** each week's bar reflects only the owner's public activities + +### Requirement: Localized chart labels +The chart SHALL render its label and per-bar distance readouts through localized strings. + +#### Scenario: German labels +- **WHEN** the UI locale is German +- **THEN** the chart label and distance readouts render in German +