Skip to content

Feature request: first-class milestone support (opt-in, backward-compatible) #8

Closed

Description

@budhash

Summary

Add a first-class milestone dimension to tasks, so work rolls up to "how close is milestone M<n>". Requested + co-sponsored by two downstream projects independently adopting a "milestone-as-contract" process (a milestone = a north-star persona + goal + feature gates + exit criteria, in docs/milestone/M<n>.md; tasks tag their milestone and roll up). Today the only option is to overload @system= / @tags=.

Hard requirement — backward compatibility

  • An existing TASKS.md with no milestone data parses + behaves exactly as today (zero migration).
  • A task without a milestone is valid → resolves to a default sentinel. Milestones are strictly opt-in; the sentinel is implicit (never written into a task line).
  • The sentinel name must not match an M<n> ID (a sentinel named m1 would make set --milestone m1 ambiguous between "assign to M1" and "clear to sentinel") — the tool must reject that.

Data model

  • Optional @milestone=<id-or-alias> tag on a task line (mirrors @branch=/@pr=/@deps=). Absent ⇒ sentinel.
  • ID vs alias: the value may be a canonical ID (m1) or an alias (alpha); the tool resolves aliases → IDs. Case-insensitive.
  • Optional # Milestones registry (a section in TASKS.md) mapping ID → alias → title → status — powers alias resolution + rollups + validation. Absent ⇒ freeform (any string accepted, no aliasing) — still fully backward-compatible.
  • Orthogonal to @system=phase-*/sprint-* and @tags= — do not overload @system.

CLI surface

Command Behavior
new task "…" --milestone m1 Assign on create. Omitted ⇒ sentinel (no tag written).
set T-N --milestone m1 Assign/change. --milestone "" clears back to the sentinel.
list --milestone m1 Filter (alias-resolved). --milestone default ⇒ the unassigned. No flag ⇒ all (today's behavior).
milestone Rollup: per-milestone counts by status + % done (+ the milestone's own status if a registry exists). Shows default as its own bucket.
milestone <M> Detail: tasks under <M> grouped by status.
next --milestone m1 (optional) next actionable within a milestone. Default next unchanged.
validate WARN (exit 0) on a @milestone= value not in the registry (when one exists); never hard-fail existing files.

Acceptance criteria

  1. An existing TASKS.md (no milestone data) round-trips byte-for-byte through any non-milestone command.
  2. With a registry mapping alpha→M1, new task --milestone alpha is listed by both list --milestone m1 and list --milestone alpha.
  3. A task with no @milestone= appears under list --milestone default + the default rollup bucket.
  4. set T-N --milestone "" removes the tag; the task returns to the sentinel bucket.
  5. milestone rollup sums correctly across ID+alias and includes default.
  6. With no registry, @milestone=anything is accepted (freeform) and grouped by raw value.
  7. validate warns (exit 0) on an unknown milestone when a registry exists; errors only on structural problems.

Co-sponsor deltas (from the second project's spec review — all nice-to-have)

  1. milestone <M> --format=json — programmatic consumption (CI / agents parse rollups). ~small; worth folding into the initial impl.
  2. --milestone-registry-path <file> — some projects prefer the registry in a separate file, not TASKS.md. Marginal; can defer.
  3. Warn on @tags=<id> when the registry has M<id> — a mechanical drift guard for the interim @tags=m1 → first-class @milestone=m1 migration path ("no task silently orphaned").

Non-goals (this iteration)

Milestone dependencies/ordering, dates, or burndown. Auto-deriving milestone status from task completion (the milestone doc's exit criteria are authoritative; the rollup is informational).


Co-sponsored feature request; two downstream projects have a full requirements spec + a reconciliation behind this. Happy to open a PR against a chosen design.

Activity

  1. budhash commented on Jul 13, 2026

    @budhash
    OwnerAuthor

    Shipped in v1.1.0 (PR #7), with parse hardening in v1.1.1 (#9/#10): @milestone= tag, optional # Milestones registry with alias resolution, new/set/list/next --milestone, milestone rollup/detail/--table, migrate-tags-to-milestone, and validate warnings. See https://github.com/budhash/tasks/releases/tag/v1.1.0 — closing. (The shadow-line tagging bug found in the field is tracked separately in #17.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions