Skip to content

Release traceability: a read-only follow-up report #17

Description

@androidand

Release traceability: a read-only follow-up report

Why

After a burst of AI-augmented work — specs that started vague, issues filed
mid-stream, commits and PRs landing fast — the hardest thing is following up.
What did we actually build? Which specs did it realize, and which work landed
with no spec at all? Which changes are effectively done and should be archived?
This is the organizing work teams say they always needed to do and always
failed to do, because reconstructing it by hand is tedious and nobody does it.

specsync already holds the pieces: the trace graph from
add-change-traceability-model knows how changes, issues, PRs, and commits
connect. This change turns that graph into a single read-only report that
answers "what happened and what's loose" — so follow-up becomes one command, not
an archaeology project.

It is a report, not a release tool. It never bumps versions, writes tags,
publishes, or owns a changelog — it detects the release tool you already use and
defers to it, adding only a recommendation and the evidence behind it.

What Changes

  • Add a release-plan command (and the lower-level trace command) that
    reports, for a revision range: the OpenSpec changes included, the issues and PRs
    linked, the commits that contributed, work that links to no spec (the loose
    ends), changes whose tasks are all done (archive candidates), and a
    human-readable summary of what shipped. Read-only by default.
  • Add an advisory SemVer signal: a recommended bump (none/patch/minor/
    major) inferred from Conventional Commit types, breaking markers/footers, and
    — the part no other tool can see — OpenSpec requirement deltas in a change's
    specs/. Every recommendation carries its reasons. It is a suggestion the
    report prints; the release tool still owns the actual bump.
  • Add light release-tool detection: probe for the common tools (release-please,
    changesets, release-it, semantic-release, standard-version, or none) and report
    which one owns bumping/tagging/changelog/publishing, so specsync visibly stays in
    its lane. Detection reads the filesystem and never invokes the tool.
  • Add one opt-in mutation: --apply performs only specsync-owned spec actions
    the report suggested — archiving a completed change. It never touches Git, the
    tracker's issues, versions, tags, or releases.

Out of scope / explicitly deferred

  • Bumping versions, writing tags, publishing, cutting GitHub releases — owned by the detected release tool, always
  • Owning or writing a changelog — the report summarizes what happened; it does not mutate CHANGELOG.md (a future opt-in could, if demanded)
  • Validation, policy modes, CI gates, hook integration — dropped: enforcement is contrary to specsync's "capture cheaply, reconcile gently" philosophy
  • A committed config file and per-project bump overrides — deferred until a real need appears (see add-change-traceability-model design)
  • Deep spec↔reality reconciliation — that is two-way-reconcile / living-plan; this report only surfaces drift and loose ends, it does not resolve them

Capabilities

New Capabilities

  • release-plan — a read-only follow-up report: what shipped, what it links to, what's loose, what's archivable, plus an advisory bump.
  • release-impact — infer an advisory SemVer bump from commit types, breaking signals, and OpenSpec requirement deltas, with reasons.
  • release-tool-detection — detect the project's release tool and report its responsibilities, deferring to it; never invoke it.

Impact

  • New code (Go, stdlib-only): semver.go (minimal parse/compare/bump),
    releaseimpact.go, releasetool.go (filesystem probes), and release-plan/
    trace subcommands in cmd/specsync. Consumes the trace model and commit source
    from add-change-traceability-model; adds no tracker mutation.
  • After this lands, the specsync skill file gains release-plan/trace so agents
    can produce a follow-up report at the end of a work session.
  • Self-test: run release-plan on this repo — it should report goreleaser + manual
    git tags as the release path, GitHub-generated notes as the changelog, and defer
    accordingly.

Tasks

Tasks: release traceability (follow-up report)

SemVer (core, semver.go)

  • Parse MAJOR.MINOR.PATCH[-pre][+build]; compare; apply a bump (none/patch/minor/major)
  • Preserve pre-release/build on read, drop on a normal bump; tests

Advisory release impact (core, releaseimpact.go)

  • Define ReleaseImpact (none|patch|minor|major) + Reasons []string
  • Map commit types (feat→minor, fix→patch, others→none); promote to major on !/BREAKING CHANGE:
  • Derive impact from OpenSpec requirement deltas (REMOVED→major, ADDED→minor, MODIFIED→patch) — the differentiating signal, sourced via the openspec CLI
  • The join: find contributing changes by walking git history in [since,until]; archive commit = the commit that moves a change out of changes/; read active-change deltas via openspec show, read archived-change delta headers (ADDED/MODIFIED/REMOVED) via git show <archive>^:<path> (the one direct-markdown read, since the CLI can't target a past ref) — do NOT treat HEAD's working-tree deltas as the whole signal
  • Pre-baseline semantics: with no accepted baseline, all deltas are ADDED (spec signal ≤ minor); note the missing baseline in output
  • Combine as the maximum across signals; attach a human reason per contributing signal
  • Fixed default mapping (no config overrides yet); tests per signal, the combination, the git-join, and the pre-baseline case

Release-tool detection (adapter, releasetool.go)

  • Filesystem probes for release-please, changesets, release-it, semantic-release, standard-version, custom, none
  • Return a light descriptor (name, detected, evidence, responsibilities owned); never import or invoke the tool
  • Neutral reporting only — name the tool and what it owns, no editorializing on which tool is better; tests over fixture trees

The report (core + cmd/specsync)

  • trace subcommand: raw resolved graph (--change, --since, --until, --json)
  • release-plan subcommand: Shipped / Loose ends / Archive candidates / Advisory bump / Release path sections (--since, --until, --json)
  • "Loose ends" from trace-graph gaps; "Archive candidates" from completed tasks.md/.status
  • Human-readable "what shipped" summary (no changelog file mutation)
  • --apply performs only spec archive actions; never Git/tracker/version/tag/release mutation
  • Per-package output when packages are configured (future); single block otherwise

Boundaries & docs

  • Read-only by default; confirm no path mutates without --apply
  • Keep boundary_test.go green (stdlib-only)
  • Add release-plan/trace to the specsync skill file with examples
  • Self-test: run release-plan on this repo; confirm it detects goreleaser + git tags + GitHub notes and defers

Related

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions