A milestone is a named delivery checkpoint in a project — a sprint, a release, or any bounded set of test activity you want to track as a unit. Milestones collect test runs, carry optional dates, and automatically compute progress from their associated runs.
Reaching the screen
Open a project, then click Milestones in the project sidebar. The URL is /projects/:projectId/milestones.
The milestones list
The list groups milestones by their parent. Each parent milestone renders as one bordered group card: a tinted parent row at the top, followed by a sub-table of its live child milestones. Parents and children are never shown as undifferentiated siblings — a child always sits inside its parent’s card. A shared column-header row sits once above the stack of cards, and the parent row and every child row line up under that same grid:
| Column | Description |
|---|---|
| Milestone | Status-colored flag icon, the milestone name (children are smaller and indented), and a one-line description beneath the name when one is set |
| Status | Color-coded lifecycle chip (Upcoming, Active, Completed) — status carries the visual weight; the list shows no progress bar |
| Dates | Plain-text dates with no urgency coloring: Finished {date} for completed milestones, Due {date} when a due date is set, No due date when only a start/forecast date exists, or No dates when none are set — with a secondary start/forecast line |
| Cases | The subtree test-case count, shown as a mono number |
| Author | The milestone author’s avatar and name, or an em-dash for API-token-created and legacy milestones |
| Per-row Edit and Delete actions (members and above) |
A right-aligned mono summary above the list reads {n} milestones · {m} sub-milestones, counting the parents and children currently shown.
On a narrow (phone-width) screen the list drops the column-header row and the group-card grid: each parent and each of its children presents as its own card, in the same order, showing the same status, dates, cases, and author values underneath the name. Every filter, sort, and action you have on a wider screen is still there — only the layout changes. At tablet width and above the list is the grouped grid described above.
Every row is clickable and opens that milestone’s detail page — clicking a child row opens the child’s own detail.
Use the search input at the top to filter by name. The status filter lets you narrow the list by one or more lifecycle states. Changing the search or status resets to the first page.
Pagination is by parent milestone
The pager below the list follows the app-wide rule: the rows-per-page selector and the range summary always show once there are enough milestones to paginate, and the previous/next and page-number controls appear only once the list spans more than one page.
The unit of pagination is the parent milestone, not the individual milestone. A page contains up to pageSize parent milestones; each parent’s children travel with it inside the same card and are never counted toward the page limit. A parent and its children are therefore never split across pages. The pager summary (X–Y of Z) counts parent milestones only — children are not included in the total.
Creating a milestone
- Click New milestone (visible to members and above; viewers see a read-only list).
- Enter a Name (required, unique within the project).
- Optionally choose a Status (defaults to
Upcoming). - Optionally set a Start date, Due date, and Forecast date. The due date must be on or after the start date — the UI enforces this before submitting.
- Optionally write a Description.
- Optionally select a Parent milestone to form a hierarchy (a milestone can only be a root or a direct child — grandchildren are not allowed).
- Click Create milestone.
If the name is already in use, an inline error appears on the name field and the dialog stays open.
Milestone status lifecycle
Milestones follow a lifecycle with four statuses:
| Status | Meaning |
|---|---|
| Upcoming | Planned but not yet started |
| Active | Currently in progress |
| Completed | All work is done; the milestone is locked for new run assignments |
| Archived | Retired; the milestone is locked and excluded from selectors |
Valid transitions are:
Upcoming→ActiveActive→CompletedCompleted→Active(re-open)- Any non-archived status →
Archived
Transitions to Completed from Upcoming (skipping Active) are not allowed. An invalid transition shows an inline error in the edit dialog.
When all associated runs are closed, the milestone auto-completes: the status transitions to Completed automatically without user intervention.
Hierarchy
A milestone can optionally have a parent milestone, forming a two-level hierarchy. This is useful for grouping related sprints under a release, or epics under a quarter milestone. Hierarchy rules:
- A root milestone has no parent.
- A child milestone has exactly one parent.
- Grandchildren (depth > 2) are not allowed.
- You cannot set a milestone as its own parent.
The parent selector in the create and edit dialogs shows only root milestones that are not the milestone being edited.
Attaching runs
Test runs can be assigned to a milestone in two ways:
- Run creation — when creating a new test run, select an optional milestone from the milestone selector. The selector shows only live non-locked (
UpcomingorActive) milestones. - Milestone detail view — open a milestone’s detail page and use the Assign run control in the Linked Runs tab.
Removing a run assignment is always available regardless of the milestone’s status — even on locked (Completed or Archived) milestones.
Progress
Progress values are computed server-side and surfaced in the detail view: a full progress bar plus stats — total runs, closed runs, and pass rate. The grouped list view intentionally shows no progress bar or percentage — status carries the weight there.
Progress updates when associated runs are closed or reassigned.
Milestone detail view
Click a milestone’s name to open its detail page (/projects/:projectId/milestones/:milestoneUlid). The page is a two-pane layout: a scrollable main column on the left and a fixed summary rail on the right.
Summary rail
The right rail always shows:
- Progress donut — percent of runs executed (closed / total).
- Results legend — individual counts for
Passed,Failed,Blocked,Skipped, andUntested, sourced from the server’s outcome counts. - Inline status control — click the pill to transition the milestone status. When the milestone is
Completed, the pill shows a check icon and is non-editable. - Date cells — Start, Due, Forecast (hidden when
Completed), and Completed dates (each shown when present, em-dash when null). - Author — the user who created the milestone (or ”—” for API-token-created or legacy milestones).
- Created / Updated timestamps.
On a phone or tablet (below 1024px) the rail is not a column. A Summary band sits directly under the page header carrying the headline readout — percent executed, the executed/total count, the failure count when there is one, and the current state — and activating it opens the whole rail as a bottom sheet. No value is dropped; only the number of taps to reach it changes.
Resizing and collapsing the summary panel
On a desktop screen (1024px and wider) the rail’s width is yours to set. Drag its leading edge — dragging left widens the rail and dragging right narrows it. With the handle focused, ArrowLeft widens by one step and ArrowRight narrows by one step, and a double-click restores the rail’s default width.
The toggle at the foot of the rail collapses it into a 56px readout column: the progress ring, the completion percentage and the executed/total count stay, a hairline separates them from a rack of icon keys, and each key opens that section’s values — pass rate, status, the date cells, author and timestamps — in a popover anchored beside the column. The live status control stays operable inside its key, so a status transition costs the same whether the rail is expanded or collapsed. Collapsing removes no value; every section is one click deeper. The same toggle, in the same place, expands the rail again.
The width and the collapsed state are shared by all four summary rails — runs, test plans, milestones and defects. Resizing or collapsing this rail changes the run, plan and defect rails too, and both settings survive a reload.
Main column — header
The header shows a back button, a breadcrumb (project › milestones › shortId), the milestone name in the display font, a clamped description with an expand affordance, a parent-milestone link (when the milestone has a parent), an Edit milestone button (members and above), and a more-actions menu.
Main column — sub-milestones band
When a milestone has children, a sub-milestones band appears below the header showing a grid of clickable child cards. Each card shows the child’s status chip, test case count, a progress bar, and closed/total runs. Clicking a card navigates to that child’s detail page. Progress values in the rail roll up to include all child milestones.
Main column — tabs
Three tabs organize the milestone’s linked items. The active tab is driven by the ?tab= URL search param (default: linked-runs).
Linked Runs tab
Shows the runs assigned to this milestone:
- An Assign run control (dropdown picker + button) when the milestone is not
Completed. - A locked banner instead of the assign control when the milestone is
Completed. - A runs table (Title / Environment / Author / Total time / Elapsed / Status / Results columns) with row-click navigation to the run detail page.
- A per-row Remove assignment button (hidden when the milestone is
Completed).
Related Test Cases tab
Shows the test cases linked to this milestone in collapsible suite-grouped bands (the same structure as the Plan detail’s Cases tab):
- Each row shows a priority chip, the display ID, the case title, and a lifecycle status chip (hidden when lifecycle is “Active” — the normal state).
- Collapsing a suite band hides all its nested child bands.
- An empty state message when no test cases are linked.
Related Defects tab
Shows the defects linked to this milestone in a read-only table with columns: number (D-n), title, status chip, severity, and assignee. Clicking a row navigates to the defect detail page. An empty state message appears when no defects are linked.
Editing a milestone
Click the Edit button (pencil icon) on a list row or the detail page to open the edit dialog. All fields are editable. The status dropdown shows only transitions that are valid from the current status.
A 409 conflict (name already in use by another milestone) shows an inline error on the name field. A 422 validation_failed shows inline errors on the offending fields.
Deleting a milestone
Click the Delete button (trash icon) on a list row. A confirmation dialog names the milestone and warns that associated runs will be unassigned. Click Delete to proceed, or Cancel to return to the list without changes.
Deletion is permanent and cannot be undone from the UI.