Stores: планирование в собственном репозитории
Бета. Stores, references, working context и worksets — новые функции. Имена команд, флаги, форматы файлов и вывод JSON могут изменяться между выпусками. Все примеры ниже выполнялись на текущей сборке, но после обновления перечитайте это руководство.
Проблема, которую это решает
Обычно OpenSpec находится внутри одного репозитория с кодом: папка openspec/ рядом с вашим кодом, содержащая спецификации и изменения для этого репозитория.
Это перестаёт подходить, как только ваше планирование становится шире одного репозитория:
- Ваша работа охватывает несколько репозиториев — одна фича затрагивает API-сервер, веб-приложение и общую библиотеку. В чьей папке
openspec/должен находиться план? - Ваша команда планирует до появления кода или планирует то, что никогда не станет кодом в этом репозитории.
- Требования принадлежат одной команде, а используются другими. Версия в вики расходится, и ваш coding agent всё равно не может её прочитать.
Store — решение: отдельный репозиторий, единственная задача которого — планирование. Он имеет ту же структуру openspec/, которую вы уже знаете — спецификации и изменения — плюс небольшой файл идентификации. Вы регистрируете его на своей машине один раз, по имени, и затем любая обычная команда OpenSpec может работать в нём из любого места.
Структура
team-plans (хранилище: планирование в собственном репозитории)
├── .openspec-store/store.yaml identity: "I am team-plans"
└── openspec/
├── specs/ что является истинным
└── changes/ что находится в работе
▲
│ регистрируется на каждой машине по имени;
│ распространяется путём push/clone как обычный репозиторий
┌─────────────┼─────────────┐
│ │ │
web-app api-server mobile-app
(code repo) (code repo) (code repo)Два правила делают это простым:
- Хранилище — это просто git-репозиторий. Вы сами коммитите, пушите, пулите и ревьюите его. OpenSpec никогда не клонирует, не синхронизирует и не пушит ничего самостоятельно.
- Декларации, а не механизмы. Репозитории могут декларировать, как они связаны с хранилищами (показано ниже). Декларации изменяют то, что OpenSpec может вам сообщить — никогда не то, где ваши команды действуют.
Пять минут до первого хранилища
Две команды ведут вас от нуля до работающего изменения, привязанного к хранилищу:
openspec store setup team-plans --path ~/openspec/team-plansStore ready: team-plans
Location: /Users/you/openspec/team-plans
OpenSpec root: ready
Registry: registered
Next: run normal OpenSpec commands against this store, for example:
openspec new change <change-id> --store team-plans
Share this store by committing and pushing it like any Git repo.openspec new change add-login --store team-plansUsing OpenSpec root: team-plans (/Users/you/openspec/team-plans)
Created change 'add-login' at /Users/you/openspec/team-plans/openspec/changes/add-login/
Schema: spec-driven
Next: openspec status --change add-login --store team-plansВот и вся модель. Отсюда жизненный цикл — это то, что вы уже знаете: status, instructions, validate, archive — с --store team-plans на каждой команде, и каждая напечатанная подсказка несёт этот флаг за вас. Строка Using OpenSpec root: всегда говорит вам, где действует команда.
История: одна команда, один репозиторий планирования
Команда хранит свои спецификации и изменения в team-plans, вместо того чтобы рассыпать их по репозиториям кода.
Первый день (тот, кто настраивает):
openspec store setup team-plans --path ~/openspec/team-plans \
--remote git@github.com:acme/team-plans.git
git -C ~/openspec/team-plans push -u origin mainПередача --remote записывает URL клонирования в собственный файл идентичности хранилища (.openspec-store/store.yaml) в начальном коммите. Каждое будущее клонирование рождается, зная, откуда оно пришло, поэтому проверки здоровья и сообщения об ошибках могут печатать полную, готовую к вставке инструкцию для коллег, у которых его ещё нет.
Каждый коллега (один раз на машину):
git clone git@github.com:acme/team-plans.git ~/openspec/team-plans
openspec store register ~/openspec/team-plansС этого момента все работают в одном репозитории планирования по имени:
openspec status --store team-plans --change add-login
openspec show add-login --store team-plansОбмен работой — это git, намеренно. Изменение, которое вы создаёте, существует только в вашем checkout, пока вы не закоммитите и не запушите его — как и код. Планы получают ветки, pull request'ы и ревью бесплатно, потому что хранилище — это обычный репозиторий.
Подключение репозиториев кода команды. Репозиторий кода, чьё планирование полностью вынесено наружу, нуждается ровно в одной строке в openspec/config.yaml:
# web-app/openspec/config.yaml
store: team-plansТеперь каждая команда OpenSpec, запущенная внутри web-app, действует на team-plans без каких-либо флагов:
cd ~/src/web-app
openspec status --change add-loginUsing OpenSpec root: team-plans (/Users/you/openspec/team-plans)
...Указатель — это запасной вариант, никогда не переопределение: явный --store всегда побеждает, и если репозиторий вырастит собственные папки планирования, они побеждают (с предупреждением удалить устаревший указатель).
Один дефолт для всех репозиториев на вашей машине. Если вы работаете с многими репозиториями кода, которые все планируют в одно хранилище, установите его один раз, глобально, вместо добавления строки store: в каждый репозиторий:
openspec config set defaultStore team-plansТеперь любая команда, запущенная вне корня планирования — и без --store и без указателя проекта — разрешается в team-plans. Она находится на дне списка приоритетов, поэтому --store, локальный корень и указатель store: проекта всё ещё побеждают. Баннер корня и блок JSON root сообщают source: "global_default" с id хранилища, поэтому вы всегда можете отличить машино-широкий дефолт от собственного указателя репозитория. Очистите его командой openspec config unset defaultStore. Если id не зарегистрировано, команды выдают ошибку и сообщают вам зарегистрировать его или очистить устаревший дефолт.
Пример: одна фича, два репозитория компонентов
Предположим, add-checkout-promo изменяет и checkout-api, и checkout-web. Команда хочет один общий продуктовый контракт, при этом каждый репозиторий кода всё ещё нуждается в собственных задачах реализации, ветке и ревью.
Используйте два уровня:
- Храните общее поведение в
team-plans. - Храните планы реализации в каждом репозитории компонента и ссылайтесь на хранилище как на контекст только для чтения.
Сначала спланируйте общий контракт в хранилище:
openspec new change add-checkout-promo --store team-plans
openspec status --change add-checkout-promo --store team-plansПредложение и спецификации должны описывать поведение на границе между компонентами — например, поля промоции, возвращаемые сервисом, и как фронтенд обрабатывает неподходящий checkout. Проведите ревью этого изменения в репозитории хранилища как любую другую ветку и pull request.
Какой контекст видит планирование?
Выбор хранилища изменяет корень OpenSpec; он не обнаруживает и не читает каждый репозиторий кода, который использует это хранилище. Инструкции хранилища видят артефакты и настроенный контекст в хранилище. Они видят код компонентов только когда эти папки также доступны агенту или редактору и агент их читает.
Workset — удобный способ открыть хранилище планирования и оба репозитория кода вместе:
openspec workset create checkout-promo \
--member ~/openspec/team-plans \
--member ~/src/checkout-api \
--member ~/src/checkout-web \
--tool code
openspec workset open checkout-promoЭто делает папки видимыми в одном рабочем пространстве IDE. Это не копирует исходный контекст в хранилище, не выбирает затронутые репозитории и не даёт агенту разрешение их редактировать. Помещайте устойчивые межкомпонентные факты в общие спецификации; не полагайтесь на то, что планировщик запомнит исходный код, который он случайно просмотрел.
Как начинается реализация в каждом репозитории?
Когда ни явный --store, ни более близкий корень openspec/ не применяются, указатель store: team-plans направляет команды в это хранилище. Он не разделяет один список задач хранилища по каталогу, из которого была вызвана apply. OpenSpec в настоящее время не направляет задачи в репозитории.
Когда каждому компоненту нужен независимо ограниченный цикл apply/review, дайте ему локальный корень OpenSpec и ссылайтесь на центральное хранилище, вместо того чтобы указывать на него:
# checkout-api/openspec/config.yaml (и аналогично в checkout-web)
schema: spec-driven
references:
- team-plansПосле того как общий контракт утверждён и доступен в основных спецификациях хранилища, создайте небольшой локальный change для части компонента:
cd ~/src/checkout-api
openspec new change implement-checkout-promo-api
cd ~/src/checkout-web
openspec new change implement-checkout-promo-uiИндекс ссылок в инструкциях каждого репозитория предоставляет сводку спецификации хранилища и точную команду получения openspec show ... --store team-plans. Каждое локальное предложение ссылается на этот общий контракт, и его задачи описывают только работу в этом компоненте. Затем запустите /opsx:apply в каждом репозитории отдельно; разрешение корня сохраняет артефакты и правки реализации в пределах этого репозитория. Изменения сервиса и фронтенда теперь могут быть протестированы, отревьюены, смержены и заархивированы независимо.
Если реализация должна начаться, пока изменение в общем хранилище ещё активно, получите его явно командой openspec show add-checkout-promo --store team-plans; индексы ссылок перечисляют канонические спецификации хранилища, а не активные изменения хранилища. Держите ветку хранилища и ветки компонентов связанными в описаниях их pull request'ов, чтобы ревьюеры могли видеть, какую версию контракта следует каждая реализация.
История: требования, пересекающие границы команд
Команда платформы владеет требованиями. Продуктовые команды строят на их основе, в своих репозиториях, со своими дизайнами. Ссылка описывает эту связь, не перемещая работу никого.
platform-reqs (хранилище) api-server (code repo)
владеет команда платформы владеет продуктовая команда
┌──────────────────────────┐ ┌──────────────────────────┐
│ openspec/specs/ │ ◀────────│ openspec/config.yaml │
│ payments/spec.md │ читает │ references: │
│ auth/spec.md │ │ - platform-reqs │
│ │ │ openspec/specs/ │
│ openspec/changes/ │ │ (их собственные дизайны)│
│ работа платформы │ │ openspec/changes/ │
│ │ │ (их собственная работа)│
│ │ └──────────────────────────┘
└──────────────────────────┘Продуктовая команда декларирует, на что она опирается в openspec/config.yaml своего репозитория:
references:
- platform-reqsСсылки — это контекст только для чтения. Репозиторий сохраняет собственный корень openspec/; работа остаётся там. Что меняется: openspec instructions в этом репозитории теперь включает индекс спецификаций ссылаемого хранилища — каждая с однострочной сводкой и точной командой получения (openspec show <spec-id> --type spec --store platform-reqs). Агент, работающий в api-server, может найти верхнепотоковые требования к платежам, сослаться на них и написать свой низкоуровневый дизайн в собственном корне репозитория — без того, чтобы кто-либо копировал контекст вручную.
Ссылка может нести источник клонирования, чтобы коллеги, у которых хранилище ещё нет, получали полную инструкцию, а не тупик:
references:
- { id: platform-reqs, remote: "git@github.com:acme/platform-reqs.git" }Когда вы хотите открыть план и код вместе, создайте workset. Это личное и явное решение: каждый выбирает папки, с которыми он действительно работает на своей машине. Ничего из этих локальных путей checkout не коммитится в общий репозиторий планирования.
openspec workset create platform \
--member ~/openspec/platform-reqs \
--member ~/src/api-server \
--member ~/src/web-appДва вопроса, которые вы всегда можете задать
"Здоров ли моя настройка?" — openspec doctor проверяет текущий корень и его ссылаемые хранилища, только для чтения, с готовой к вставке инструкцией для каждого обнаружения:
Doctor
Root
Location: /Users/you/src/api-server
OpenSpec root: ok
References
- platform-reqs: ok (/Users/you/openspec/platform-reqs)
- design-system: Referenced store 'design-system' is not registered on this machine.
Fix: git clone -- git@github.com:acme/design-system.git '/Users/you/openspec/design-system' && openspec store register '/Users/you/openspec/design-system' --id design-system"С чем я работаю?" — openspec context собирает рабочий набор из деклараций OpenSpec: корень и хранилища, на которые он ссылается.
Working context for api-server (/Users/you/src/api-server)
OpenSpec root
api-server /Users/you/src/api-server
Referenced stores
platform-reqs /Users/you/openspec/platform-reqs
Fetch: openspec show <spec-id> --type spec --store platform-reqsОбе поддерживают --json для агентов. openspec context --code-workspace <path> дополнительно записывает файл рабочего пространства VS Code, содержащий весь набор — единственная запись, которую выполняет эта команда.
Worksets: снова открывайте папки, с которыми работаете вместе
Отдельно от всего вышеперечисленного: большинство людей в каждой сессии открывают одни и те же несколько папок — репозиторий планирования плюс два-три репозитория кода. Workset — это персональное именованное представление именно этого набора, которое открывается одной командой в вашем инструменте.
workset "platform" openspec workset open platform
├── team-plans ~/openspec/team-plans │
├── api-server ~/src/api-server ▼
└── web-app ~/src/web-app все три открываются в вашем инструментеopenspec workset create platform \
--member ~/openspec/team-plans --member ~/src/api-server \
--tool code
openspec workset listplatform (открывается в VS Code)
team-plans /Users/you/openspec/team-plans
api-server /Users/you/src/api-serveropenspec workset open platform затем запускает сохранённый инструмент: редакторы (VS Code, Cursor) открывают одно окно со всеми участниками и завершают работу. Первый участник является основным. В любой момент можно переопределить инструмент с помощью --tool <id>.
Worksets намеренно не являются общим состоянием. Они хранятся на вашей машине, никогда не коммитятся и ничего не говорят о самой работе — они лишь фиксируют, какие папки вы обычно открываете вместе. Удаление workset'а никогда не затрагивает папки участников. Новые инструменты — это конфигурация, а не код: всё, что запускается через файл рабочей области или флаги привязки к папкам, можно добавить в ключ openers в глобальной конфигурации (openspec config edit).
Как команды определяют, где действовать
Каждая обычная команда определяет свой корень одинаковым образом, в следующем порядке:
1. --store <id> вы указали явно → тот store
2. ближайший openspec/ реальный корень планирования → этот репозиторий
(поднимаясь из cwd)
3. store: pointer config.yaml объявляет store → тот store
4. defaultStore глобальный конфиг задаёт → тот store
машинный дефолт
5. ни один из выше на этой машине зарегистрированы → ошибка с
перечисленных store'ы? подсказкой выбора
нет зарегистрированных store'ов? → текущий каталог
(классическое поведение)Строка Using OpenSpec root: (и блок root в выводе --json) сообщает, в каком случае вы находитесь.
Известные ограничения
- Бета-форма. Всё на этой странице может меняться между релизами — имена, флаги, форматы файлов, ключи JSON.
- Один checkout на store id на машину. Регистрация второго checkout под тем же id завершается ошибкой с подсказкой сначала выполнить
store unregister. - Никогда не синхронизируется — так задумано. OpenSpec никогда не клонирует, не тянет и не пушит. Устаревший checkout показывает устаревшие спецификации, пока вы не выполните pull; ссылки индексируются на лету из того, что есть на диске.
- Пустые папки планирования могут отсутствовать. Новый store может ещё не иметь
openspec/changes/,openspec/specs/илиopenspec/changes/archive/в Git. Это допустимо в период беты; эти папки появятся, когда обычные команды создадут для них файлы. - Репозитории-указатели остаются указателями. Репозиторий только с конфигурацией, у которого в
openspec/config.yamlобъявленоstore: <id>, рассматривается как внешний план, а не как локальный checkout store'а. Если вы намеренно хотите преобразовать такой репозиторий в локальный корень store'а, сначала удалите строкуstore:. - Некоторые команды остаются на месте.
templatesи устаревшие именные формы (openspec change show, ...) действуют только на текущий каталог — без--store.schemasследует каноническому порядку выбора корня и принимает--store <id>, сохраняя неизменной свою успешную структуру JSON-массива. - Состояние на машине существует только на этой машине. Реестр store'ов и workset'ы — локальные настройки. Никаких сведений о структуре вашей машины никогда не попадает в общий план.
- Два способа запуска workset'ов. Инструмент, который не может быть запущен через файл рабочей области или флаги привязки к папкам, не может быть добавлен как opener.
- В JSON агента есть известное расхождение в регистрации ключей (ключи семейства store — snake_case, семейства workflow — camelCase). Это описано в agent contract; унификация отложена до версионированного релиза.
Где что находится
| Что | Где | Общий? |
|---|---|---|
| План store'а | <store>/openspec/ (спецификации, изменения) | Да — коммитьте и пушите |
| Идентичность store'а | <store>/.openspec-store/store.yaml | Да — коммитится вместе со store |
| Реестр store'ов | <data dir>/openspec/stores/registry.yaml | Нет — только на этой машине |
| Worksets | <data dir>/openspec/worksets/ | Нет — только на этой машине |
<data dir> — это ~/.local/share/openspec на macOS и Linux (или $XDG_DATA_HOME/openspec, если задано) и %LOCALAPPDATA%\openspec на Windows.
Справочник
Точные флаги и JSON-структуры для каждой команды на этой странице: CLI reference (Stores, Doctor, Working context, Personal worksets) и agent contract.