Follow-up to #1846 / #1849.
Ask: Let changes be grouped under a namespace folder the way specs already can (changes/mobile/refresh-token/ next to specs/mobile/tutorial-videos/), so one shared store can keep each product's changes together.
Today: #1849 makes a nested change fail loudly instead of being silently mishandled. Flat names like mobile-refresh-token are a temporary workaround.
Proposed design
Treat a change id as a path, just like a spec capability path.
| Area |
Behavior |
| Id grammar |
One or more kebab-case segments joined by / (mobile/refresh-token). Each segment passes the existing isKebabId check. No .., leading/trailing /, or empty segments. Flat ids stay valid, so nothing existing breaks. |
| What counts as a change |
A directory that holds .openspec.yaml (which openspec new change always writes). Any other folder on the way down is a namespace. This replaces the #1849 heuristic with one rule. |
| Commands |
new, list, show, status, instructions, validate, and archive all accept the full path id. list groups changes by namespace. |
| Archive |
Keeps the namespace: changes/archive/mobile/2026-09-18-refresh-token/. It moves only the change directory, never a namespace folder or sibling changes. |
| Completion |
Suggests full path ids. |
Why not the alternatives: a --directory flag splits one piece of information across two inputs and has to be repeated on every command. Keeping flat names forever leaves changes/ and specs/ organized differently for no reason.
Guardrail: archive must never move or drop an unfinished nested change (the #1846 data-loss case), and there should be a regression test for that.
Follow-up to #1846 / #1849.
Ask: Let changes be grouped under a namespace folder the way specs already can (
changes/mobile/refresh-token/next tospecs/mobile/tutorial-videos/), so one shared store can keep each product's changes together.Today: #1849 makes a nested change fail loudly instead of being silently mishandled. Flat names like
mobile-refresh-tokenare a temporary workaround.Proposed design
Treat a change id as a path, just like a spec capability path.
/(mobile/refresh-token). Each segment passes the existingisKebabIdcheck. No.., leading/trailing/, or empty segments. Flat ids stay valid, so nothing existing breaks..openspec.yaml(whichopenspec new changealways writes). Any other folder on the way down is a namespace. This replaces the #1849 heuristic with one rule.new,list,show,status,instructions,validate, andarchiveall accept the full path id.listgroups changes by namespace.changes/archive/mobile/2026-09-18-refresh-token/. It moves only the change directory, never a namespace folder or sibling changes.Why not the alternatives: a
--directoryflag splits one piece of information across two inputs and has to be repeated on every command. Keeping flat names forever leaveschanges/andspecs/organized differently for no reason.Guardrail:
archivemust never move or drop an unfinished nested change (the #1846 data-loss case), and there should be a regression test for that.