Recorded activities often carry garbage points at the ends (forgot to stop the tracker and drove off; walked from the truck to the trailhead before starting). Let the user open a completed activity and trim points from either end, with a one-tap "suggested trim" that detects the sustained speed jump of driving.
Locked decisions:
New POST api/recorded-routes/{id}/trim — body is the range to KEEP as inclusive upload-order indices (matches the analysis-contract index space): {keepStartIndex, keepEndIndex, expectedPointCount}. 200 → full detail DTO; 400 out-of-range vs actual points-doc length / kept<2; 404 foreign id; 409 expectedPointCount mismatch (retry/idempotency guard). No-op → 200 no writes.
Semantics everywhere: inclusive slice; segment rebase (p.segment -= kept[0].segment); stats recomputed via the pinned RouteStatsCalculator formulas (untouched).
Write order (no transactions): delete recorded-route-analysis doc first (stale inclusive indices are the hazard; doc self-describes as safe-to-delete and lazily rematerializes) → replace PointsJson → recompute summary from the points doc (retry self-heals).
Suggested trim is a client-side pure function ported 3× like TrackStats (works offline on unsynced routes): arc speeds (segment-boundary arcs = 0; speedMps else haversine/dt), tail+leading scans, threshold 6.0 m/s (11.0 bike), sustain ≥20 s, tolerating ≤30 s slow breaks (traffic lights). Slow walk-to-trailhead deliberately not auto-detected — manual handles cover it.
UI on all clients: dual-thumb range slider bound to point indices + live map preview (kept polyline normal, trimmed ends dimmed, boundary handles), before/after stats, confirm dialog with exact delta. MapLibre Native has no draggable-annotation story, so no on-map dragging.
Synced routes must trim via the endpoint — re-flipping to pending-upload would duplicate server-side. Unsynced routes trim locally. Web is the trim surface for routes recorded on any device.
One canonical fixture trace; expectations duplicated verbatim into all four test suites.
No server-side undo v1 (snapshot-original is a possible follow-up).
Children: backend endpoint (lands first), then iOS / Android / Angular in parallel.
Child issues (per-repo, auto-close on merge)
This epic is complete when every box below is checked. Each child lives in the
repo that holds its fix, so fixes #N in a PR closes it automatically.
Checklist generated by the umbrella-tracker migration, 2026-08-07 — Opus 5 Agent
Recorded activities often carry garbage points at the ends (forgot to stop the tracker and drove off; walked from the truck to the trailhead before starting). Let the user open a completed activity and trim points from either end, with a one-tap "suggested trim" that detects the sustained speed jump of driving.
**Locked decisions:**
- New `POST api/recorded-routes/{id}/trim` — body is the range to **KEEP** as inclusive upload-order indices (matches the analysis-contract index space): `{keepStartIndex, keepEndIndex, expectedPointCount}`. 200 → full detail DTO; 400 out-of-range vs actual points-doc length / kept<2; 404 foreign id; 409 `expectedPointCount` mismatch (retry/idempotency guard). No-op → 200 no writes.
- Semantics everywhere: inclusive slice; segment rebase (`p.segment -= kept[0].segment`); stats recomputed via the pinned `RouteStatsCalculator` formulas (untouched).
- Write order (no transactions): delete `recorded-route-analysis` doc **first** (stale inclusive indices are the hazard; doc self-describes as safe-to-delete and lazily rematerializes) → replace `PointsJson` → recompute summary from the points doc (retry self-heals).
- Suggested trim is a **client-side pure function** ported 3× like TrackStats (works offline on unsynced routes): arc speeds (segment-boundary arcs = 0; `speedMps` else haversine/dt), tail+leading scans, threshold 6.0 m/s (11.0 bike), sustain ≥20 s, tolerating ≤30 s slow breaks (traffic lights). Slow walk-to-trailhead deliberately not auto-detected — manual handles cover it.
- UI on all clients: dual-thumb range slider bound to point indices + live map preview (kept polyline normal, trimmed ends dimmed, boundary handles), before/after stats, confirm dialog with exact delta. MapLibre Native has no draggable-annotation story, so no on-map dragging.
- Synced routes must trim via the endpoint — re-flipping to pending-upload would duplicate server-side. Unsynced routes trim locally. Web is the trim surface for routes recorded on any device.
- One canonical fixture trace; expectations duplicated verbatim into all four test suites.
- No server-side undo v1 (snapshot-original is a possible follow-up).
Children: backend endpoint (lands first), then iOS / Android / Angular in parallel.
<!-- BEGIN MIGRATED-CHILDREN -->
---
## Child issues (per-repo, auto-close on merge)
This epic is complete when every box below is checked. Each child lives in the
repo that holds its fix, so `fixes #N` in a PR closes it automatically.
**iOS**
- [ ] spikerj/spikersoft-ios#16 — iOS: route trim UI + RouteTrimmer (epic #992) <sub>(was #994)</sub>
<sub>Checklist generated by the umbrella-tracker migration, 2026-08-07 — Opus 5 Agent</sub>
<!-- END MIGRATED-CHILDREN -->
The unit is tracked by the shared [Route trim] title prefix and by sibling
cross-links in each issue body. Closing here — the umbrella tracker is being
emptied.
— Opus 5 Agent
Dissolved into per-repo issues as part of the umbrella-tracker breakup.
This epic could never auto-close from a merge, because Gitea only resolves
`fixes #N` within a single repo. Its work now lives where the code is:
**iOS**
- spikerj/spikersoft-ios#16 — iOS: route trim UI + RouteTrimmer
**Already shipped** — verified against master and live prod during the migration, no issue filed:
- spikerj/spikersoft-issues#993 (complete)
- spikerj/spikersoft-issues#995 (complete)
- spikerj/spikersoft-issues#996 (complete)
The unit is tracked by the shared `[Route trim]` title prefix and by sibling
cross-links in each issue body. Closing here — the umbrella tracker is being
emptied.
— Opus 5 Agent
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Recorded activities often carry garbage points at the ends (forgot to stop the tracker and drove off; walked from the truck to the trailhead before starting). Let the user open a completed activity and trim points from either end, with a one-tap "suggested trim" that detects the sustained speed jump of driving.
Locked decisions:
POST api/recorded-routes/{id}/trim— body is the range to KEEP as inclusive upload-order indices (matches the analysis-contract index space):{keepStartIndex, keepEndIndex, expectedPointCount}. 200 → full detail DTO; 400 out-of-range vs actual points-doc length / kept<2; 404 foreign id; 409expectedPointCountmismatch (retry/idempotency guard). No-op → 200 no writes.p.segment -= kept[0].segment); stats recomputed via the pinnedRouteStatsCalculatorformulas (untouched).recorded-route-analysisdoc first (stale inclusive indices are the hazard; doc self-describes as safe-to-delete and lazily rematerializes) → replacePointsJson→ recompute summary from the points doc (retry self-heals).speedMpselse haversine/dt), tail+leading scans, threshold 6.0 m/s (11.0 bike), sustain ≥20 s, tolerating ≤30 s slow breaks (traffic lights). Slow walk-to-trailhead deliberately not auto-detected — manual handles cover it.Children: backend endpoint (lands first), then iOS / Android / Angular in parallel.
Child issues (per-repo, auto-close on merge)
This epic is complete when every box below is checked. Each child lives in the
repo that holds its fix, so
fixes #Nin a PR closes it automatically.iOS
Checklist generated by the umbrella-tracker migration, 2026-08-07 — Opus 5 Agent
Dissolved into per-repo issues as part of the umbrella-tracker breakup.
This epic could never auto-close from a merge, because Gitea only resolves
fixes #Nwithin a single repo. Its work now lives where the code is:iOS
Already shipped — verified against master and live prod during the migration, no issue filed:
The unit is tracked by the shared
[Route trim]title prefix and by siblingcross-links in each issue body. Closing here — the umbrella tracker is being
emptied.
— Opus 5 Agent