Skip to content

[Feature]: Stable, block-allocated identifiers (FR/SC/T/CHK) so edits never force a renumber #4065

Description

@ira-at-work

Problem Statement

FR-###, SC-###, T### and CHK### are references, not just labels. Once a spec exists, its identifiers get cited from plan.md, tasks.md dependency lines, checklist items ([Spec §FR-001]), analyze.md findings tables, converge.md source-refs, the GitHub issue titles created by /speckit.taskstoissues, commit messages, and PR review comments.

But the templates and commands tell agents to allocate them densely and sequentially:

  • templates/commands/tasks.md:160 — "Task ID: Sequential number (T001, T002, T003...) in execution order"
  • templates/commands/checklist.md:258 — "globally incrementing IDs starting at CHK001"
  • templates/checklist-template.md:45 — "Items are numbered sequentially for easy reference"
  • templates/spec-template.mdFR-001FR-007, SC-001SC-004 run as one dense sequence

Dense + sequential means any insertion or deletion forces a renumber, and a renumber silently invalidates every citation elsewhere. This shows up in practice in several ways:

  1. Cross-artifact reference rot. Insert FR-004 into an existing spec and every later FR shifts. checklists/requirements.md, plan.md, and any open PR comment now point at the wrong requirement. Nothing errors — the references just quietly mean something else.
  2. Token cost, twice over. Adding one task at position 10 of a 50-task file rewrites 40 task lines plus every dependency reference (depends on T012, T013) and every summary block. Deleting a task is worse: the renumber-and-re-reference pass is pure waste, and it contaminates the agent's context with a diff that carries no information.
  3. Agents get it wrong at scale. #1497 reported exactly this: after deleting three tasks from a 100-task file the task lines were renumbered "more or less correctly" but the By Type: … (T009-T021, T036-T039, T046-T049) summaries went out of sync and were hard to repair.
  4. It contradicts a rule the repo already has. templates/commands/converge.md:77 forbids the agent to "rewrite, renumber, reorder, or delete any existing task", and :219 says "Never reuse or renumber existing IDs." That invariant is exactly right — but it currently only applies to convergence tasks, while the dense allocation scheme in tasks.md / spec-template.md / checklist.md actively pushes agents in the opposite direction everywhere else.

#1497 was closed as stale rather than rejected on the merits, so the underlying problem is still live.

Proposed Solution

Make identifiers permanent, and allocate them sparsely enough that they can stay permanent.

1. Identifiers are permanent references. Never renumber an existing identifier. A removed item's number is retired, not reused, and the hole it leaves is never closed. Gaps are the expected steady state, not damage to repair. (This is converge.md's existing rule, promoted to a general invariant.)

2. Allocate in 1000-blocks per group, stepping by 10 within the group. Each group — an FR category, a task phase, a checklist category — starts at the next multiple of 1000; items step by 10 inside it:

## Phase 1: Setup
- [ ] T1000 Create project structure per implementation plan
- [ ] T1010 Initialize [language] project with [framework] dependencies
- [ ] T1020 [P] Configure linting and formatting tools

## Phase 2: Foundational
- [ ] T2000 Setup database schema and migrations framework
- [ ] T2010 [P] Implement authentication/authorization framework

The two levels of spacing do different jobs:

  • Step 10 → insert in position. A task that belongs between T1010 and T1020 becomes T1015. It sits where it should in the document, IDs stay ascending, and nothing after it moves. This is the case a per-group block alone does not solve.
  • 1000-blocks → groups are independent. Appending to Phase 1 takes T1030; Phase 2 is untouched. Adding a whole phase takes the next unused thousand.
  • Deletion → do nothing. Remove the line and stop. No renumber, no re-reference pass, no context churn.

For an ungrouped list, the same step-10 rule applies from 1000.

Concretely:

  • templates/spec-template.md — FR samples grouped by category (FR-1000, FR-1010, … / FR-2000, …); SC follows the same scheme; both carry the rule in a template comment
  • templates/tasks-template.md — sample tasks renumbered per phase; the ## Format section documents block, step, insert, and delete
  • templates/checklist-template.mdCHK1000 / CHK2000 per category; the closing note changes from "numbered sequentially for easy reference" to "stable references — do not renumber"
  • templates/commands/{specify,clarify,tasks,checklist,converge}.md — generation rules updated to allocate, insert, and preserve accordingly

One dependent fix. With four-digit IDs, templates/commands/taskstoissues.md:67 breaks: it matches issue titles with `\bT\d{3}\b`exactly three digits — so given T1000 the trailing \b cannot fall between two digits and there is no match at all. Those tasks are silently neither deduplicated nor converted into issues. This is already reachable today, independent of this proposal (#3866): converge.md formats IDs with T{M+1:03d}, and 03d is a floor rather than a cap. Widening to `\bT\d{3,}\b` is a prerequisite here, and recording the contract in converge.md keeps producer and consumer from drifting apart again.

Alternatives Considered

  • Hierarchical IDs (T1.1, T1.2) — the #1497 proposal. Solves the same insertion problem, but changes the ID shape. Every consumer that assumes T\d+ breaks: taskstoissues.md's regex and issue titles, converge.md's T{M+1:03d} formatting, analyze.md's tables, and any existing tasks.md in the wild. Inserting between T1.2 and T1.3 also still needs a sub-level (T1.2.1), so IDs grow unboundedly rather than staying uniform. Numeric blocks keep the existing T#### shape and every existing consumer keeps working.
  • 100-blocks with step 1 (T100, T101 / T200). Shorter IDs and solves append/delete, but not positional insert — a task belonging between T101 and T102 has no free number, so it either renumbers or lands out of order.
  • 100-blocks with step 10 (T100, T110). Solves both, but caps a group at ~10 items before it overflows into the next block; task phases routinely exceed that.
  • Do nothing / tell agents "try not to renumber". Roughly the status quo, and Hierarchical Task Numbering System #1497's report shows agents renumber anyway when the surrounding template models dense sequential allocation. The template has to make the correct behaviour the path of least resistance.
  • Renumber, but auto-update references. Requires a reference index across spec.md, plan.md, tasks.md, checklists, and external systems (GitHub issues, PR comments) that no tool can reach. Not feasible for the external half.

Component

Spec templates (BDD, Testing Strategy, etc.)

AI Agent (if applicable)

All agents

Additional Context

I've been running this convention locally (constitution rule + patched templates and command prompts) and it removed the renumbering churn entirely.

PR implementing the above: #4066. Existing artifacts are not retroactively renumbered — the rule applies going forward. Full test suite is unchanged by the diff (13 failed / 6616 passed both with and without it; the 13 are pre-existing branch-slug and template-composition failures on main).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions