Skip to content

Release v0.2.0 - #76

Merged
roncodes merged 36 commits into
mainfrom
release/v0.2.0
Sep 9, 2026
Merged

Release v0.2.0#76
roncodes merged 36 commits into
mainfrom
release/v0.2.0

Conversation

@roncodes

@roncodes roncodes commented Sep 8, 2026

Copy link
Copy Markdown
Member

Release branch for v0.2.0. Feature PRs targeting this release are retargeted here and merge into this branch; merging this PR into main triggers the release-tag workflow, which tags v0.2.0 from package.json.

Included

Release prep in this branch

  • package.json bumped 0.1.400.2.0
  • RELEASE.md seeded with the v0.2.0 header and highlights (placeholders removed so the tag job passes)

Update RELEASE.md before merging if the included set changes.

roncodes and others added 30 commits June 17, 2026 13:59
Moved here from @fleetbase/ember-core, where it had been importing
../models/waypoint — a path that does not exist in that package, so the module
threw for anyone who imported it.

This is its natural home: the Waypoint model lives here, so the import is a
plain relative one. It could not stay in ember-core, because ember-core cannot
depend on fleetops-data (fleetops-data already depends on ember-core), leaving
only an undeclared cross-package import or an optional peer dependency, both
worse than putting the util beside the model it checks.

No package used it in source, so nothing needs updating alongside this. The
matches that show up elsewhere are in built dist bundles, which is ember-core's
own code inlined into consumers' vendor files rather than real usage.

Tested against a real waypoint, the place model waypoint extends, an unrelated
record, a proxy, and plain and nullish values.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Ember test suite could not boot at all: `@ember/string` is only a peer
dependency of ember-data, so it was never linked at the top level and the
vendor bundle could not resolve it. CI only ran `pnpm run build`, so this
went unnoticed. Declare it explicitly, along with the other pieces the dummy
application needs to stand up the addon:

- `ember-fetch`, which `@fleetbase/ember-core`'s fetch service imports and
  host applications supply.
- `API.host` / `API.namespace` in the dummy config, read at module scope by
  the ember-core application adapter.
- Stub models for the 11 types Fleet-Ops relates to but does not define
  (file, user, company, comment, custom-field-value, ...).
- `EXTEND_PROTOTYPES: true`, matching the Fleetbase console, which Fleet-Ops
  models rely on for `pushObjects`/`objectAt` on plain arrays.

Coverage is provided by ember-cli-code-coverage, instrumenting only the
addon's own trees so the report contains first-party source and nothing
else. `scripts/check-coverage.js` enforces 100% globally and per file, and
derives the eligible file list from disk so a module that never gets
instrumented fails the gate instead of vanishing from the denominator.
`tests/helpers/load-addon-modules.js` force-loads every addon module and
rethrows any import failure rather than swallowing it.

Baseline: 175 tests / 131 pass / 44 fail. Now: 556 tests / 556 pass,
40.69% statements, 39.05% branches, 33.57% functions, 39.81% lines.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replace the generated "look up a serializer, serialize an empty record,
assert truthy" stubs with tests of the actual wire contract: the exact
`attrs` map (so an added or removed embed fails), `uuid` as the primary key,
attribute round trips through normalize and serialize, embedded relationships
travelling inline, and non-embedded ones linked by `<key>_uuid`.

The driver serializer gets its own treatment: user, vendor, current_job,
fleets and jobs are server-owned and must never be echoed back, while the
assigned vehicle is embedded — and the defensive path that reduces an
already-collected `vehicle` to an identifier is exercised directly.

Also move the Codecov upload ahead of the coverage gate so a shortfall is
still reported on the pull request instead of being hidden behind a failed
step, and fix the Feature test that was asserting the wrong constructor
branch (a Point carries type and coordinates, so it never reaches the
`instanceof GeoJson` path — a GeometryCollection does).

644 tests, 644 pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replace the remaining generated "create a record, assert it exists" model
stubs with the structural contract each model actually declares:

- the full relationship map, including async, polymorphic and inverse
  options, so a mis-wired relationship fails loudly rather than surfacing as
  a runtime error in a consumer;
- configured attribute defaults, and separately that image defaults resolve
  through `defaultValues` configuration rather than being hard-coded;
- every `date-fns` formatting getter family, asserted against literal
  expected strings for a fixed local instant and against a relative instant
  for the distance getters, plus null/undefined/Invalid Date on both sides of
  the `isValidDate` guard;
- the shared GeoJSON point accessors on place, driver and vehicle, including
  null island, zero components and the coordinate extremes.

The three models whose date getters have no `isValidDate` guard (payload,
integrated-vendor, service-rate) are asserted only on the happy path, with a
comment recording that a missing date throws there rather than rendering as
null — documenting real behaviour instead of an aspiration.

766 tests, 766 pass. Coverage 80.18% statements, 78.75% branches,
76.59% functions, 80.30% lines; 40 of 130 files still below 100%.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Completes the campaign: the coverage gate now passes with every eligible
addon file at 100% statements, branches, functions and lines.

Behavioural coverage for the parts the structural generators deliberately
left alone: the service-rate fee ranking, dedup and rule builders; manifest
and manifest-stop status lifecycles and progress arithmetic; position speed
and timestamp conversion; device, sensor and attachable display fallbacks;
the driver, vehicle, fuel-report and issue relationship loaders; zone and
service-area geography; and the custom serialize / normalize /
serializePolymorphicType overrides on the order, waypoint, maintenance,
work-order, maintenance-schedule, entity, device and service-rate
serializers.

Fifteen defensive clauses cannot be reached through any supported use — a
`rankFee` rank for a record state Ember Data cannot produce, `?? []` guards
on relationships that always expose `toArray`, and guards already implied by
the check above them. Rather than weaken the gate or edit production code,
`scripts/unreachable-code.js` lists each one by its exact Istanbul location
with the reason it is unreachable. The gate counts those locations as
covered, prints them on every run, and fails if one becomes reachable or
disappears — so an exemption cannot outlive its cause. Nothing else is
excluded.

DEFECTS.md records all fifteen findings: four production defects fixed
earlier in the campaign, six behavioural issues left alone with the test that
pins each one, and the five unreachable-code entries.

986 tests, 986 pass, 0 skip. Lint, production build and the 38 coverage-gate
self-tests all pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Points at the default branch, so it reports the repository's coverage once
this branch merges. Also records, in DEFECTS.md, that the README claims MIT
while package.json and LICENSE.md say AGPL-3.0-or-later.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`package.json` declares `AGPL-3.0-or-later` and `LICENSE.md` carries the GNU
Affero General Public License v3 text; only the README said MIT. It now names
the licence the project actually ships under.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Declare attachment_state and vehicle_id on the Trailer model; the console
  never received the attachment state because the attribute was missing.
- Add displayName, isAttached, isOnline, and formatted last-online/attached-at
  projections to Trailer, and formatted timing plus duration to AssetConnection.
- Add the attachable-driver model so equipment issued to a driver normalizes
  instead of failing on an unknown model.
- Set explicit inverses on the vehicle trailer/equipment relationships and
  embed the trailer category.
- Cover query and single-record envelope normalization, the new projections,
  and the driver mapping.
The live map feed now sends each vehicle's currently coupled trailers alongside
its devices so map popovers can list both. Declare the relationship as embedded
so normalization pushes the trailer records instead of treating the objects as
ids, and cover it with a normalizeResponse test.
Bump package.json to 0.2.0 and seed RELEASE.md so the release-tag workflow
can validate the version once this branch merges to main.
…-rate

Their date-fns getters formatted updated_at/created_at without the
isValidDate guard every sibling model uses, so reading them on an unsaved
record threw RangeError: Invalid time value. They now return null like the
rest of the addon, and the guarded: false escape hatch in the shared test
helper is gone so no model can opt out again.
serializeBelongsTo special-cased a vehicle that was already an array in the
outgoing json and wrote vehicle_uuid from get(array, 'uuid'), which is
always undefined. The branch could never run during record.serialize()
either, because the embedded records mixin writes json.vehicle after this
hook, and nothing in the monorepo calls the hook directly. Removed rather
than repaired: correcting the type test would have left it just as dead.
The four @Not macros referenced camelCase getters the model never declared,
so missing_tracking_number, missing_purchase_rate, missing_tracking_statuses
and missing_payload were always true. They now negate the has_* presence
macros declared directly above them. No consumer in the monorepo reads the
flags, so nothing had adapted to the always-true values.
The shim re-exported a default the barrel never declares, so anything
resolving it through the app tree got undefined. It now re-exports the
barrel's named surface, matching the module it points at.
The post-save reconciliation keyed persisted fees by id and drafts by shape,
so the two key spaces never intersected and per-drop and fixed-distance
drafts were never removed once the backend returned their saved
counterparts, leaving duplicate rows in the editor until reload. Both sides
are now keyed by shape; genuinely different fees still do not collapse.
driver.loadVehicle/loadVendor, fuelReport.loadVehicle/loadDriver and
issue.loadVehicle/loadDriver guarded their fetch with ember-core's
isRelationMissing, which passes an empty path to its second isset and so
never consults the relationship: every call re-fetched. They now use a local
isRelationMissing that is the exact negation of shouldNotLoadRelation.

shouldNotLoadRelation itself now reads a declared relationship through its
Ember Data reference rather than the property: the driver's and fuel
report's relationships are async, and an async belongsTo hands back a
promise proxy that is never blank, so a plain property check would have
made those loaders never fetch at all. Every relationship the order loaders
check is async: false, so their behaviour is unchanged.

The upstream ember-core util is still broken for its other consumers; fixing
it there is out of scope here and noted in DEFECTS.md.
serializePolymorphicType read belongsTo.modelName before its own null
guard, so an unset customer threw a TypeError and the guard could never
run. The read now sits inside the else branch like every sibling
serializer, an unset customer writes customer_type: null, and the coverage
exemption for the formerly dead branch is removed.
… above

loadPayload's second early return needed a payload whose waypoints was
neither a ManyArray nor an array, which Ember Data cannot produce, and the
_index_resource flag existed only to feed it. loadCustomer repeated the
exact condition shouldNotLoadRelation had just returned false for. Both
are gone, along with their coverage exemptions.
… relationship

Ember Data never lets a hasMany be anything but a ManyArray, so the branch
returning this.waypoints when it lacked toArray could not run. The getter
now materializes the relationship unconditionally and the exemption and
the tests that documented the dead branch are removed.
… fallbacks

rankFee had a middle rank for a fee that is neither new nor has an id,
which an Ember Data record cannot be, and was copied into three getters. It
is now one module-level helper with two ranks. The ?? [] fallbacks guarded
rate_fees and parcel_fees against having no toArray, which a hasMany
always has. Their coverage exemptions go with them.
super resolves lexically against the prototype chain and JSONSerializer
always provides the hook, so the typeof guard and its bare return could
never run. With this last entry gone scripts/unreachable-code.js exports an
empty map; the gate test now checks that an empty map prints no exemption
block, and the file keeps its header as the documented home for any future
exemption.
…report keys

A file the report showed with all-zero totals skipped every metric and
counted as fully covered, so an empty entry could stand in for a module
that was never instrumented. Zero branches or functions are still skipped,
but a file with zero statements is now parsed and must genuinely contain
none (a bare subclass, a class of decorated fields, a re-export barrel);
anything with executable code fails, and every declaration-only file is
listed in the gate output.

Two report keys normalizing onto the same addon path used to overwrite
each other silently, so a dummy-app fixture with the same name as an addon
module could substitute its trivial coverage for the real thing. A
collision now fails the gate and names both keys.
…the build

pull_request.branches filters on the base branch and both triggers listed
only main, so pull requests retargeted at release/v0.2.0 ran no lint,
tests, coverage gate or build, and pushes to the release branch triggered
nothing. Both triggers now include release/**.

fail_ci_if_error turned a rate-limited tokenless Codecov upload from a
forked pull request into a failed test job even when everything else had
passed; the upload is reporting and the gate is enforcement, so it is now
false. The unused test:ci script, a second definition of what CI runs that
could drift from the workflow, is removed.
Every recorded defect is now fixed, so the document records what was wrong,
what changed and the covering test for each, and adds the coverage-gate and
CI problems fixed in the same series.
Test coverage campaign: 100% coverage gate, CI and Codecov
Add is-waypoint-record util (moved from ember-core)
roncodes and others added 5 commits September 9, 2026 10:21
Merging the four v0.2.0 pull requests left @ember/optional-features and
@ember/string keyed without their (supports-color@8.1.1) suffix in the
importer section while the package section still carried it, so
pnpm install --frozen-lockfile failed with ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY.
The two lines are restored from the last consistent lockfile.
… serializer's dead guard

The device tests used trailer as their unsupported attachable type, which
the trailer work made supported; they now use place and assert trailer
resolves. The equipment contract test still expected no embedded attrs.
The is-waypoint-record test resolved its util through the dummy app tree,
where ember-core's same-named shim wins and imports a waypoint model
ember-core does not have; it now imports the addon module directly.

The equipment serializer carried the same unreachable
typeof super.serializePolymorphicType guard removed from the device
serializer, with its domain-type restore folded into normalize. It now
mirrors the device serializer's structure so every branch is testable.
The inspection form, submission and item-result models and serializers had
no tests; several trailer and asset-connection getters were never read.
DEFECTS.md records the merge findings, including the ember-core
is-waypoint-record shim collision this repository cannot fix.
@codecov

codecov Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@c4323c4). Learn more about missing BASE report.

Additional details and impacted files
@@           Coverage Diff            @@
##             main       #76   +/-   ##
========================================
  Coverage        ?   100.00%           
========================================
  Files           ?       114           
  Lines           ?      2899           
  Branches        ?       687           
========================================
  Hits            ?      2899           
  Misses          ?         0           
  Partials        ?         0           
Flag Coverage Δ
fleetops-data 100.00% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
addon/models/asset-connection.js 100.00% <100.00%> (ø)
addon/models/asset.js 100.00% <ø> (ø)
addon/models/attachable.js 100.00% <ø> (ø)
addon/models/driver.js 100.00% <ø> (ø)
addon/models/equipment.js 100.00% <ø> (ø)
addon/models/fuel-report.js 100.00% <ø> (ø)
addon/models/inspection-form.js 100.00% <100.00%> (ø)
addon/models/inspection-submission.js 100.00% <100.00%> (ø)
addon/models/integrated-vendor.js 100.00% <100.00%> (ø)
addon/models/issue.js 100.00% <ø> (ø)
... and 22 more

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c4323c4...7b7167a. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Every defect it recorded is fixed and covered by tests, and the working
notes are not something to ship in the package. The three comments that
pointed at it now carry their explanation inline.
@roncodes
roncodes merged commit 7831acc into main Sep 9, 2026
8 checks passed
@roncodes
roncodes deleted the release/v0.2.0 branch September 9, 2026 04:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant