github / github/spec-kit

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

Open
#4,065 15 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
137k
Forks
12.3k
Avg merge
2d 12h
Merged PRs (30d)
159

Description

### 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.md` — `FR-001`…`FR-007`, `SC-001`…`SC-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](https://github.com/github/spec-kit/issues/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:

```markdown
## 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.md` — `CHK1000` / `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](https://github.com/github/spec-kit/issues/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](https://github.com/github/spec-kit/issues/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 #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`).

Contributor guide

Open the contributing guide

Research direction

Start with PR #4066 and compare the changes against templates/spec-template.md, templates/tasks-template.md, templates/checklist-template.md, and the five command files named in the issue. Check the taskstoissues.md regex and existing converge.md rule, then verify that future identifiers remain stable and four-digit task IDs are recognized without retroactively changing existing artifacts.

Written by the indexing model from the issue text.

Assessment

Tech stack
markdown
Domain
developer-experience, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.