Repository navigation
[Bug]: Re-running specify init --here --force resets feature_numbering in init-options.json to "sequential" #5098
Description
Bug Description
When specify init runs in an existing project, it reads the previous .specify/init-options.json and merges it into the new options (src/specify_cli/command_init.py:850-858, {**previous_init_options, ...}). Custom keys survive. However, "feature_numbering": "sequential" is then set unconditionally, so a project configured for timestamp feature numbering silently switches back to sequential numbering after a re-init.
The expectation is inferred rather than documented. templates/commands/specify.md:87-89 documents feature_numbering in init-options.json as the setting that selects "timestamp" or "sequential" prefixes. There is no specify init flag that sets it, because --branch-numbering was removed (tests/test_branch_numbering.py). Editing the file is therefore the only way to choose timestamp numbering, and the merge suggests user-set options are meant to persist. I found no doc or test that says re-init must keep this key.
Steps to Reproduce
From a fresh clone, after uv sync, save the script below and run uv run python -I repro.py from the repository root. It runs specify init twice in a temporary directory through the CLI app, and sets feature_numbering to "timestamp" between the two runs.
import json, os, tempfile
from pathlib import Path
from typer.testing import CliRunner
from specify_cli import app
proj = Path(tempfile.mkdtemp())
os.chdir(proj)
args = ["init", "--here", "--force", "--integration", "claude", "--script", "sh",
"--non-interactive", "--ignore-agent-tools"]
runner = CliRunner()
assert runner.invoke(app, args).exit_code == 0
opts_path = proj / ".specify" / "init-options.json"
opts = json.loads(opts_path.read_text())
opts["feature_numbering"] = "timestamp"
opts["my_custom_key"] = "kept?"
opts_path.write_text(json.dumps(opts))
print("before:", {k: opts[k] for k in ("feature_numbering", "my_custom_key")})
assert runner.invoke(app, args).exit_code == 0
after = json.loads(opts_path.read_text())
print("after: ", {k: after.get(k) for k in ("feature_numbering", "my_custom_key")})Expected Behavior
feature_numbering: "timestamp" should survive a re-init, as my_custom_key does. This is inferred from the option merge at src/specify_cli/command_init.py:850-851 and the documentation of the setting at templates/commands/specify.md:87-89.
Actual Behavior
before: {'feature_numbering': 'timestamp', 'my_custom_key': 'kept?'}
after: {'feature_numbering': 'sequential', 'my_custom_key': 'kept?'}
Specify CLI Version
1.2.4.dev0 (main at 27d14e2)
AI Agent
Not applicable
Operating System
Linux 6.18 (Ubuntu container)
Python Version
Python 3.12.3
Additional Context
A likely fix is to default feature_numbering to "sequential" only when the previous options do not already set it.
Also reproduced on macOS (Darwin 25.6, arm64) with Python 3.14.4 and uv run python -I repro.py against 27d14e2; the output matches the above.
AI Disclosure
Found and reproduced with Claude Code (model: Claude Opus 4.8), run autonomously under human supervision. The human reporter re-ran every reproduction above before filing.
Activity