Thanks, nice to get some feedback and it seems we can improve a lot here, making it toward 0.3 then 
1. Dates without a time of day, and the series shape
Agree on the problem. What do you think about making all intra-activity time defined as seconds since the activity’s true start:
{ "metric": "heartRate", "unit": "/min", "bucket": "5s",
"samples": [ { "offset": 0, "avg": 121, "min": 118, "max": 124, "count": 5 }, … ] }
Route points become { "lat", "lon", "offset" } on the same clock. A consumer with a precise startTime adds the two and loses nothing; a consumer without one still gets the chart.
On top of that, quant:timePrecision: "second" (default) or "day". I would keep startTime itself an AS2 xsd:dateTime (local midnight with offset under "day") rather than a bare date, since typed consumers may reject a date where a dateTime is declared, and that is the kind of breakage §10 is written to avoid. Under "day", receivers MUST NOT render a time of day, and endTime is omitted. The one rule that makes this safe: offsets count from the true start, never from the midnight placeholder.
Consequence for the §6 endpoint: a series addressed by absolute window makes no sense when the window is hidden, so it becomes per post:
GET {apiBase}/public/{username}/feed/{postId}/series/{metric}?bucket=5s
The four-condition check collapses to two (post publicly visible, metric opted in as a series), and the “can the caller widen the window” question disappears with the window parameters. Bucket floor, no-store and the 404 discipline stay as they are.
2. Source
Agreed, we could add source as suggested with device and via, and a SHOULD for receivers to show it.
3. One unit per key
Yes. Every RECOMMENDED key could get a fixed unit, SI where one exists, written as a UCUM code so we point at a standard instead of our own list. unit is then omitted (or equal to the canonical one) on recommended keys, and REQUIRED on implementation-defined keys.
| key |
unit |
duration |
s |
distance, elevationGain |
m |
heartRateAvg, heartRateMax |
/min |
calories |
kcal |
speedAvg (replaces paceAvg) |
m/s |
steps |
1 (count) |
Aurboda currently sends distance in km and would then change.
4. Extension keys
There is a standard for extending properties (JSON-LD prefixes in @context, which is how quant: itself works), but quant:metrics is a JSON literal, so keys inside it are opaque strings and nothing expands them. So the spec has to say what a prefixed key means:
- A key is either a RECOMMENDED bare key or
prefix:name, where the prefix resolves to an IRI. vertmatch:tep resolving to https://vertmatch.run/ns#tep makes that IRI the documentation link, so no separate about.
- Resolution via the object’s
@context at Level 2+, and for Level 1 payloads a prefixes map in /.well-known/quantpub, which peers already fetch.
label optional. Receivers MAY render an unknown key as label plus value, MUST NOT guess its unit, and MUST NOT treat a prefixed key as any recommended key even when the local name matches.
5. Activity type hierarchy
What do you think about adopting the FIT format’s types? It’s Garmin sourced, but has become somewhat of an industry standard. Like:
quant:activityType is the parent, a FIT sport key. Readers always understand this one.
quant:activitySubtype (OPTIONAL) is the specific kind, a FIT sub_sport key, with implementation-defined values allowed through the same prefix:name mechanism as extension metric keys.
- A reader that does not know the subtype falls back to the type. No registry, no walking.
HealthKit and Health Connect are both flat lists with no parent relation, so they give a vocabulary but no fallback; FIT is the one that has the hierarchy and the installed base. The lists are open and I would reference them rather than copy them.
6 Segments
Sounds good; with your shape adjusted to the new clock so segments, series and route all align:
"segments": [
{ "kind": "climb", "name": "Rumer Alm", "id": "https://example.app/climbs/123",
"startOffset": 1140, "endOffset": 3840,
"startDistance": 3200, "endDistance": 7800,
"metrics": [ { "key": "elevationGain", "value": 640 }, { "key": "duration", "value": 2700 } ] }
]
kind is free text for now (climb, descent, lap, interval), id optional, and the per-segment metrics reuse the §2.1 shape and unit rules so nothing new has to be learned. Segments are part of the activity share, not a separate opt-in; a publisher that considers a segment’s location too revealing leaves id and name out.
7 and 8. Agreed and going in: peers MUST drop the stored payload on Delete and refetch on Update (dropping on 404); route points carry offsets only when a series is also shared; the payload says nothing about whether or how much a route was masked. Your protected-places practice would make a good non-normative paragraph in Security considerations if you want to write it.
0.3 will then be a breaking change to the series shape and endpoint path.
I can draft this up in a PR if you agree to the solutions?