Skip to content

feat(templates): allocate FR/SC/T/CHK identifiers sparsely and treat them as permanent - #4066

Open
ira-at-work wants to merge 1 commit into
github:mainfrom
ira-at-work:feat/4065-stable-block-identifiers
Open

feat(templates): allocate FR/SC/T/CHK identifiers sparsely and treat them as permanent#4066
ira-at-work wants to merge 1 commit into
github:mainfrom
ira-at-work:feat/4065-stable-block-identifiers

Conversation

@ira-at-work

Copy link
Copy Markdown

Implements #4065. See that issue for the full problem statement and the alternatives that were weighed.

Per CONTRIBUTING.md this touches templates and command prompts, so it needs maintainer agreement before merge. Opening it alongside the issue so the proposal is concrete rather than hypothetical — happy to trim, split, or drop it.

Description

Spec Kit identifiers are references, not labels. plan.md, tasks.md dependency lines, checklist items ([Spec §FR-001]), analyze.md tables, converge.md source-refs, the GitHub issues created by /speckit.taskstoissues, commit messages and PR comments all cite them.

The templates nevertheless allocate them densely and sequentially (tasks.md:160 — "Sequential number (T001, T002, T003...)"; checklist.md:258 — "globally incrementing IDs starting at CHK001"). Dense + sequential means every insert or delete forces a renumber, and a renumber silently invalidates every citation elsewhere while burning tokens rewriting lines that carry no new information.

#1497 reported the failure mode concretely — deleting three tasks from a 100-task file renumbered the task lines "more or less correctly" but left the (T009-T021, T036-T039, T046-T049) summaries pointing at the wrong work. It was closed as stale, not rejected on the merits.

The repo already has the right invariant: converge.md:77 forbids the agent to "rewrite, renumber, reorder, or delete any existing task" and :219 says "Never reuse or renumber existing IDs." It just applies to convergence tasks only, while everything around it models the opposite behaviour. This PR generalises that invariant and gives it the room it needs to hold.

What changes

Two levels of spacing, each doing a different job.

rule what it buys
Group each FR category / task phase / checklist category starts at the next multiple of 1000 groups are independent — appending to Phase 1 never touches Phase 2
Item items step by 10 within a group positional insert: a task between T1010 and T1020 becomes T1015, IDs stay ascending, nothing after it moves
Delete remove the line and stop no renumber, no re-reference pass, no context churn — gaps are the steady state
 ## Phase 1: Setup
-- [ ] T001 Create project structure per implementation plan
-- [ ] T002 Initialize [language] project with [framework] dependencies
-- [ ] T003 [P] Configure linting and formatting tools
+- [ ] T1000 Create project structure per implementation plan
+- [ ] T1010 Initialize [language] project with [framework] dependencies
+- [ ] T1020 [P] Configure linting and formatting tools

 ## Phase 2: Foundational
-- [ ] T004 Setup database schema and migrations framework
+- [ ] T2000 Setup database schema and migrations framework

Applied to FR, SC, T and CHK alike:

  • templates/spec-template.md — FR samples grouped by category (FR-1000… / FR-2000…), SC on the same scheme, both with the rule stated in a template comment
  • templates/tasks-template.md — sample tasks renumbered per phase; ## Format documents block, step, insert and delete
  • templates/checklist-template.mdCHK1000 / CHK2000 per category; 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 into gaps, and preserve
  • docs/concepts/complex-features.md — the one example range that would otherwise contradict the templates

Convergence phases now open their own block (next unused multiple of 1000) instead of continuing from T{M+1:03d}, so a converge run cannot land inside a phase's range.

One dependent fix. taskstoissues.md:67 matched issue titles with `\bT\d{3}\b`exactly three digits. Given T1000 the trailing \b cannot fall between two digits, so there is no match at all and those tasks are silently neither deduplicated nor created. This is already reachable on main (#3866) because converge.md's 03d is a floor rather than a cap; four-digit IDs make it unavoidable. Widened to `\bT\d{3,}\b`, with the contract recorded in converge.md so producer and consumer can't drift apart again.

input \bT\d{3}\b \bT\d{3,}\b
T001 T001 T001
T1000 T1000
T12345 T12345
ST1000
T1

Note the word boundaries still do their original job: T100 can never match inside T1000, because the trailing \b forces the whole digit run to be consumed.

Compatibility

  • ID shape is unchanged (T + digits, FR- + digits). Nothing that parses the existing format breaks — unlike the hierarchical T1.1 scheme proposed in Hierarchical Task Numbering System #1497.
  • Existing artifacts are not retroactively renumbered. Old T001-style files keep working; the rule applies to newly generated and newly edited artifacts.
  • tests/hooks/tasks.md is deliberately left at T001 for that reason.

Testing

.venv/bin/python -m pytest tests -q -p no:randomly
  • with this branch: 13 failed, 6616 passed, 177 skipped
  • on clean main: 13 failed, 6616 passed, 177 skipped

Identical — the 13 are pre-existing failures unrelated to this change (branch-slug generation in test_timestamp_branches.py / test_git_extension.py, and template-composition parity in test_resolve_template_python_parity.py and friends). Verified by stashing the diff and re-running.

tests/test_specify_template_numbering.py (top-level step ordinals in specify.md) passes — that test governs markdown list numbering, which this PR does not touch.

…them as permanent

Spec Kit identifiers are references, not labels: plans, tasks, checklists
(`[Spec §FR-001]`), analyze findings, converge source-refs, the GitHub issues
created by /speckit.taskstoissues, commit messages and PR comments all cite
them. But the templates allocate them densely and sequentially, so any insert
or delete forces a renumber -- and a renumber silently invalidates every one of
those citations while burning tokens rewriting lines that did not change.

github#1497 reported the failure mode concretely: deleting three tasks from a
100-task file renumbered the task lines "more or less correctly" but left the
`(T009-T021, T036-T039)` summaries pointing at the wrong work. The repo already
has the correct invariant -- converge.md forbids renumbering and reusing IDs --
but it applies only to convergence tasks while the surrounding templates model
the opposite behaviour everywhere else.

Generalise the invariant and give it room to work:

- Each group (FR category, task phase, checklist category) starts at the next
  multiple of 1000; items step by 10 within the group.
- Insert into the gap: an item between T1010 and T1020 becomes T1015. Nothing
  after it shifts, so document order stays ascending without a renumber.
- Append at the next free multiple of 10; open a new group at the next unused
  multiple of 1000. Editing one group never touches another.
- Removal leaves a permanent hole. Gaps are the steady state, not damage to
  repair, and a retired number is never re-issued.

taskstoissues.md matched issue titles with `\bT\d{3}\b` -- exactly three digits
-- so a title containing T1000 did not match at all and those tasks were
silently neither deduplicated nor created. That is already reachable today via
converge.md's `T{M+1:03d}` (a floor, not a cap; see github#3866) and unavoidable
under phase blocks, so widen it to `\bT\d{3,}\b` and record the contract in
converge.md so producer and consumer cannot drift apart again.

Existing artifacts are not retroactively renumbered; the rule applies going
forward.

Refs github#4065, github#1497, github#3866
@mnriem

mnriem commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

See comment in #4065

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants