- Expose a stable package-level public API (
FitFile,FitFileBuilder, exceptions, version constants) viafrom fit_tool import ..., documented in the README. Existing deep imports remain supported. (#SHA-5) - Introduce a wire-layer MVP (
fit_tool/wire) with raw header/record models and a stateful decoder that keeps immutable definition snapshots.FitFile.from_bytesdecodes via the wire layer and projects to existing typed messages while keeping public API behavior compatible. (#SHA-7) - Unify stream and in-memory FIT decoding on one state machine (
FitDecoderover the wire layer) so definition snapshots, developer-field registration, and CRC handling stay aligned betweenFitFile.from_bytesanditer_*. (#SHA-8) - Add a composable validation API (
validate_fit_file,FitFile.validate) with WIRE / PROFILE / FILE_TYPE levels and report or raise modes. Builderstrict=Truenow delegates to the same checks. (#SHA-9) - Split generated message construction into explicit create (
MessageClass()) and decode (MessageClass.from_definition(...)) paths; MessageFactory uses the definition factory. (#SHA-10) - Expand decode-time component / accumulator coverage to all Profile main-field
sources via a generated registry (
fit_tool/profile/component_registry.py), with nested expansion (e.g. compressed speed → enhanced speed) and modular accumulator rollover. Subfield-gated components remain deferred to subfield work. (#SHA-15) - Fix Profile subfield resolution: match reference field values (AND across multi-ref maps), apply the active subfield's type/scale/offset/units, expand components declared on that subfield, and report ambiguous multi-matches as PROFILE validation errors (decode still uses the first match). (#SHA-16)
- Retain unknown native field ids on known messages during decode as
UnknownField(withraw_bytesfor later PRESERVATION rewrite). Uneditedto_bytes(preserve=True)remains bit-identical viawire_document. (#SHA-17) - Post-edit PRESERVATION: per-record dirty tracking (field mutations mark
Record.dirty) soto_bytes(preserve=True)re-encodes only edited records and copiessource_bytesfor the rest (unknown fields and other records survive). Opt-inConformanceLevel.PRESERVATIONreports loss when unknown-fieldraw_byteswere cleared. Structuralmark_dirty()/ add / remove still force a full projected re-encode. (#SHA-18) - Encode policies: explicit
EncodeMode.PRESERVE/EncodeMode.CANONICALonFitFile.to_bytes(legacypreserve=still works). Canonical rebuilds all records with normalized sizes/CRCs;strict=Truevalidates first and never clamps invalid values. Policy matrix documented in README and design doc §6. (#SHA-19) - PROFILE validation supports selectable scopes (
ProfileScope.CORE/DOMAIN/FULL). Defaultstrict/DEFAULT_LEVELSremain CORE (developer fields + ambiguous subfields). DOMAIN and FULL add data-driven native base-type and closed-enum checks from a gen-exported field catalog (fit_tool.profile.field_catalog) derived from the bundled Profile. FULL is opt-in only viavalidate_fit_file(..., profile_scope=ProfileScope.FULL). (#SHA-20) - FILE_TYPE validation for Workout files: required
workout/workout_stepmessages and fields (num_valid_steps, stepmessage_index/duration_type/target_type). Activity behavior unchanged; otherfile_id.typevalues (e.g. Course) still fail closed. SDK Workout fixtures validate clean under FILE_TYPE. (#SHA-21) - FILE_TYPE validation for Course files: required course / lap / record / timer events and fields (aligned with Garmin Course rules and real device exports). Workout and other non-Activity/Course types still fail closed. (#SHA-22)
- Protocol high-severity fixes: chained multi-segment FIT decode, trailing-byte rejection, compressed-timestamp reconstruction, known component expansion, and wire preservation encode for unedited buffer-decoded files. (#sha3)
- Validate FIT header CRC when the header is larger than 12 bytes (CRC of the preceding header bytes in the final two bytes; gated by
check_crc). (#sha3-medium) - Add bidirectional FIT interoperability coverage against the Garmin JavaScript SDK at the bundled Profile version, plus corrected generated Profile scales. (#33)
- Copy record-header
local_idonto parsed definition and data messages, and fix FLOAT field encoding so fractional values are preserved and FIT invalid all-ones bit patterns round-trip asNone. (#sha3)
- Move the detailed capability matrix and validation/encode notes from the
README into
docs/CAPABILITY_BOUNDARY.md. The README now keeps a short install, public API, and minimal examples section, with a link to that doc. (#readme-capability-docs) - Document the protocol fixture corpus and gap inventory under
fit_tool/tests/data/README.md, with constructive golden tests for component edges, unknown fields on known messages, and subfield-bearing workout steps. (#SHA-14) - Document the current capability boundary for readers: README Supported / Partial / Not-supported matrix and a short “what this library still does not claim” section now match the shipped code (PROFILE scopes CORE/DOMAIN/FULL, Activity/Workout/Course FILE_TYPE validation, post-edit PRESERVE/CANONICAL encode). The conformance design doc and residual checklist are re-synced so public claims stay honest — full Garmin FIT / Profile conformance is still not advertised until every Definition of Done item is met. (#SHA-23)
- Document the maintainer release checklist and harden the tag-based PyPI/GitHub
Release workflow (version gate,
v-prefix tags, changelog extraction, split build/publish/github-release jobs). (#SHA-36)
- Move profile-generation packages (
openpyxl,inflection, plusjinja2) to an optional[gen]extra and drop unusedbitstructfrom the runtime install. (#sha6) - Upgrade the bundled Garmin FIT SDK Profile to 21.212.0. (#60)
- Deprecate
Record.from_bytesfor full-file decode. PreferFitFile.from_bytes/WireDecoder+ compatibility; the method remains for isolated record pack/unpack tests. (#sha3-medium)
- Raise typed
Fit*errors on core parse/encode paths and improve CLI error exits; align package metadata with Python 3.9+ and Ruff as a CI gate. (#sha3-hygiene)
- Optimize
Field.to_byteswithb''.joinfor better performance. (#4) - Remove redundant record re-encoding verification from
FitFile.from_bytesfor improved performance. (#10) - Add proper logging for unknown base types in profile generation. (#17)
- Add uv support and modernize CLI. (#18)
- Switch the build toolchain to UV (#19)