Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ encode policies): [`docs/CAPABILITY_BOUNDARY.md`](docs/CAPABILITY_BOUNDARY.md).
Long-term strict-conformance **roadmap** (target architecture, not current
guarantees): [`docs/FIT_CONFORMANCE_DESIGN.md`](docs/FIT_CONFORMANCE_DESIGN.md).

Profile in use: `21.205.0` (`fit_tool.SDK_VERSION`).
Profile in use: `21.212.0` (`fit_tool.SDK_VERSION`).

## Installation

Expand Down
4 changes: 2 additions & 2 deletions docs/CAPABILITY_BOUNDARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ strict-conformance **target** architecture remains
[`FIT_CONFORMANCE_DESIGN.md`](FIT_CONFORMANCE_DESIGN.md) (not a guarantee of
what the installed package already does).

Bundled Profile version: **`21.205.0`** (`fit_tool.SDK_VERSION` / `fit_tool/gen/`).
Bundled Profile version: **`21.212.0`** (`fit_tool.SDK_VERSION` / `fit_tool/gen/`).
Protocol version string: **`2.4`** (`fit_tool.PROTOCOL_VERSION`).

---
Expand Down Expand Up @@ -58,7 +58,7 @@ this package against the matrix above, not against full protocol conformance.
| Level | What it checks | Status in code |
| --- | --- | --- |
| `ConformanceLevel.WIRE` | Local IDs, definition field layout/sizes, data records vs active definition | Implemented |
| `ConformanceLevel.PROFILE` | Scoped Profile semantics via `profile_scope=` / `ProfileScope` | **CORE (default):** developer-field declarations + **ambiguous native subfields** as ERROR. **DOMAIN (opt-in):** CORE + native base-type and closed-enum checks on high-frequency Activity/Workout messages. **FULL (opt-in):** same native rules for the gen-exported catalog from Profile.xlsx `21.205.0`. FULL is never default `strict` (§3.1 O1). Open/ranged enums (e.g. `activity_class`) are excluded from closed-enum checks |
| `ConformanceLevel.PROFILE` | Scoped Profile semantics via `profile_scope=` / `ProfileScope` | **CORE (default):** developer-field declarations + **ambiguous native subfields** as ERROR. **DOMAIN (opt-in):** CORE + native base-type and closed-enum checks on high-frequency Activity/Workout messages. **FULL (opt-in):** same native rules for the gen-exported catalog from Profile.xlsx `21.212.0`. FULL is never default `strict` (§3.1 O1). Open/ranged enums (e.g. `activity_class`) are excluded from closed-enum checks |
| `ConformanceLevel.FILE_TYPE` | `file_id` first/unique + required fields; Activity, Workout, and Course rules | **`IMPLEMENTED_FILE_TYPES`** = Activity, Workout, Course; other `file_id.type` values **fail closed** |
| `ConformanceLevel.PRESERVATION` | Post-edit rewrite loss (e.g. `UnknownField.raw_bytes` cleared) | **Opt-in** — not in `DEFAULT_LEVELS` / Builder `strict=True` |

Expand Down
2 changes: 1 addition & 1 deletion docs/EPIC_SHA12_RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ subfields, unknown fields, post-edit PRESERVATION, encode policies, PROFILE
scopes CORE/DOMAIN/FULL, Workout + Course FILE_TYPE). Stage 5 (**L**) publishes
honest Supported/Partial/Not-supported claims.

Profile / SDK version: **`21.205.0`** (`SDK_VERSION`, `fit_tool/gen/`).
Profile / SDK version: **`21.212.0`** (`SDK_VERSION`, `fit_tool/gen/`).

## User-visible capabilities (rollup)

Expand Down
6 changes: 3 additions & 3 deletions docs/FIT_CONFORMANCE_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public-claims pass that keeps this table and `docs/CAPABILITY_BOUNDARY.md` hones
| Unknown field ids on known messages (decode retain + raw bytes) | Phase 3 | **E** SHA-17 | **Done** `UnknownField` + `raw_bytes` on main path |
| Post-edit PRESERVATION (edited files, dirty records) | Phase 4 / PRESERVATION level | **F** SHA-18 | **Done** per-record dirty + mixed encode; opt-in PRESERVATION findings |
| Encode policies (canonical vs preserve, strict vs repair) | Phase 4 §6 | **G** SHA-19 | **Done** `EncodeMode` + policy matrix; no silent invalid clamp; `repair()` still future |
| PROFILE scopes from bundled Profile.xlsx `21.205.0` | Phase 3 PROFILE / §3.1 | **H** SHA-20 | **Done** CORE/DOMAIN/FULL + gen `field_catalog`; default remains CORE; native required fields / units still residual |
| PROFILE scopes from bundled Profile.xlsx `21.212.0` | Phase 3 PROFILE / §3.1 | **H** SHA-20 | **Done** CORE/DOMAIN/FULL + gen `field_catalog`; default remains CORE; native required fields / units still residual |
| Workout FILE_TYPE rules | Phase 4 §7 | **I** SHA-21 | **Done** required messages/fields; SDK Workout*.fit pass FILE_TYPE |
| Course FILE_TYPE rules | Phase 4 §7 | **J** SHA-22 | **Done** Course required messages/fields; Activity + Workout unchanged |
| Public capability matrix / release-note pass | Phase 5 | **L** SHA-23 | **This pass** — README + this section + §11 residual checklist + Towncrier epic notes |
Expand Down Expand Up @@ -82,7 +82,7 @@ Passing one dimension must not be reported as passing all four.

The conformance target is the Garmin FIT Protocol documentation and the exact
`Profile.xlsx` version committed under `fit_tool/gen/`. The current repository
uses Profile `21.205.0`.
uses Profile `21.212.0`.

Official references:

Expand Down Expand Up @@ -791,7 +791,7 @@ Honest status vs the bullets above. **None** of these residuals re-open C–J as
| Garmin cross-validation | **Incomplete** | SDK samples used as smokes/fixtures; not a release gate for full interop |
| Non-`xfail` tests for listed constructs | **Partial** | Strong constructive coverage for C–J; expand goldens / remove any remaining known-gap xfails as follow-up |
| Strict mode no silent repair | **Met** | `strict=True` / DEFAULT_LEVELS never clamp or auto-repair; no `repair()` on strict path |
| Published SDK/Profile version | **Met** | Profile / SDK `21.205.0` (`fit_tool/gen/`, `SDK_VERSION`) |
| Published SDK/Profile version | **Met** | Profile / SDK `21.212.0` (`fit_tool/gen/`, `SDK_VERSION`) |

**Bottom line:** SHA-12 may move to `done` / `in_review` with the residuals
above explicit. Do **not** flip public marketing to “full Garmin FIT
Expand Down
2 changes: 1 addition & 1 deletion fit_tool/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# import ``from fit_tool import SDK_VERSION`` during package initialization
# (e.g. fit_file_header) see a fully assigned name.
PROTOCOL_VERSION = '2.4'
SDK_VERSION = '21.205.0'
SDK_VERSION = '21.212.0'
FIT_DATA_TYPE = b'.FIT'

from fit_tool.api import ( # noqa: E402
Expand Down
Binary file removed fit_tool/gen/Profile_21.205.0.xlsx
Binary file not shown.
Binary file added fit_tool/gen/Profile_21.212.0.xlsx
Binary file not shown.
2 changes: 1 addition & 1 deletion fit_tool/profile/component_registry.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
# Main-field components only (subfield-gated components omitted — see Stage 2 D).
"""Profile component registry for decode-time expansion.

Expand Down
9 changes: 5 additions & 4 deletions fit_tool/profile/field_catalog.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
"""Native field and closed-enum catalog for PROFILE validation.

Generated from the bundled Garmin Profile spreadsheet by ``gen-profile``.
Expand All @@ -16,9 +16,9 @@

from fit_tool.base_type import BaseType

PROFILE_SDK_VERSION = '21.205.0'
PROFILE_SDK_VERSION = '21.212.0'
PROFILE_MESSAGE_COUNT = 123
PROFILE_FIELD_COUNT = 1406
PROFILE_FIELD_COUNT = 1407
PROFILE_ENUM_TYPE_COUNT = 100

# (global_message_number, field_id) → (name, BaseType, type_name, units, scale, offset)
Expand Down Expand Up @@ -713,6 +713,7 @@
(55, 32): ('descent', BaseType.UINT32, 'uint32', 'm', 1000.0, 0.0), # monitoring
(55, 33): ('moderate_activity_minutes', BaseType.UINT16, 'uint16', 'minutes', 1.0, 0.0), # monitoring
(55, 34): ('vigorous_activity_minutes', BaseType.UINT16, 'uint16', 'minutes', 1.0, 0.0), # monitoring
(55, 41): ('pushes', BaseType.UINT32, 'uint32', '', 1.0, 0.0), # monitoring
(55, 253): ('timestamp', BaseType.UINT32, 'date_time', 'ms', 0.001, -631065600000.0), # monitoring
(72, 0): ('type', BaseType.ENUM, 'file', '', 1.0, 0.0), # training_file
(72, 1): ('manufacturer', BaseType.UINT16, 'manufacturer', '', 1.0, 0.0), # training_file
Expand Down Expand Up @@ -1438,7 +1439,7 @@
'activity': frozenset({0, 1}),
'activity_level': frozenset({0, 1, 2}),
'activity_subtype': frozenset({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 254}),
'activity_type': frozenset({0, 1, 2, 3, 4, 5, 6, 8, 254}),
'activity_type': frozenset({0, 1, 2, 3, 4, 5, 6, 8, 13, 254}),
'analog_watchface_layout': frozenset({0, 1, 2}),
'ant_network': frozenset({0, 1, 2, 3}),
'attitude_stage': frozenset({0, 1, 2, 3}),
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/aad_accel_features_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -110,6 +110,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/accelerometer_data_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -152,6 +152,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/activity_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -124,6 +124,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/ant_channel_id_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -103,6 +103,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/ant_rx_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -110,6 +110,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/ant_tx_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -110,6 +110,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/aviation_attitude_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -152,6 +152,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/barometer_data_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -96,6 +96,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/beat_intervals_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -89,6 +89,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/bike_profile_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -292,6 +292,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/blood_pressure_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -145,6 +145,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/cadence_zone_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -89,6 +89,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/camera_event_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -103,6 +103,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/capabilities_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -96,6 +96,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/chrono_shot_data_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -89,6 +89,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
4 changes: 3 additions & 1 deletion fit_tool/profile/messages/chrono_shot_session_message.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Autogenerated. Do not modify.
#
# Profile: 21.205.0
# Profile: 21.212.0
from typing import Optional

from fit_tool.base_type import BaseType
Expand Down Expand Up @@ -124,6 +124,8 @@ def from_definition(cls, definition_message: DefinitionMessage,
growable=False)
])
message.growable = False
# Keep definition field ids not present in the Profile schema (Stage 2 E).
message.retain_unknown_fields()
return message

@classmethod
Expand Down
Loading
Loading