Kho lưu trữ (Stores): Lập kế hoạch trong một kho mã riêng biệt
Beta. Các khái niệm về kho lưu trữ (stores), tham chiếu, ngữ cảnh làm việc và bộ làm việc (worksets) là mới. Tên lệnh, cờ (flags), định dạng tệp và đầu ra JSON có thể thay đổi giữa các bản phát hành. Mọi hướng dẫn dưới đây đều được thực hiện trên bản dựng hiện tại, nhưng hãy đọc lại tài liệu hướng dẫn này sau khi nâng cấp.
Vấn đề mà tính năng này giải quyết
Thông thường, OpenSpec được đặt trong cùng một kho mã: một thư mục openspec/ nằm cạnh mã nguồn của bạn, chứa các đặc tả (specs) và các thay đổi cho kho mã đó.
Tuy nhiên, cách tiếp cận này không còn phù hợp ngay khi công tác lập kế hoạch của bạn vượt quá phạm vi của một kho mã duy nhất:
- Công việc của bạn trải rộng trên nhiều kho mã — một tính năng có thể ảnh hưởng đến máy chủ API, ứng dụng web và một thư viện dùng chung. Vậy thư mục
openspec/thuộc về kho mã nào để chứa kế hoạch? - Đội ngũ của bạn lên kế hoạch trước khi mã nguồn tồn tại, hoặc lên kế hoạch cho những thứ sẽ không trở thành mã nguồn trong kho này.
- Yêu cầu do một đội ngũ sở hữu nhưng được các đội ngũ khác sử dụng. Phiên bản trên wiki dễ bị lệch pha, và agent lập trình của bạn cũng không thể đọc được nó.
Kho lưu trữ (Store) chính là câu trả lời: một kho mã độc lập với nhiệm vụ duy nhất là lập kế hoạch. Nó có cấu trúc openspec/ tương tự như những gì bạn đã quen thuộc — bao gồm các đặc tả và các thay đổi — cùng với một tệp định danh nhỏ. Bạn chỉ cần đăng ký kho này trên máy của mình một lần bằng tên, và sau đó mọi lệnh OpenSpec thông thường đều có thể hoạt động trên kho này từ bất kỳ đâu.
Hình dạng (The shape)
team-plans (một kho lưu trữ: lập kế hoạch trong repo riêng của nó)
├── .openspec-store/store.yaml identity: "I am team-plans"
└── openspec/
├── specs/ những gì là đúng đắn
└── changes/ những gì đang diễn ra
▲
│ được đăng ký trên mỗi máy theo tên;
│ được chia sẻ bằng cách đẩy/kéo về như bất kỳ kho nào khác
┌─────────────┼─────────────┐
│ │ │
web-app api-server mobile-app
(kho mã nguồn) (kho mã nguồn) (kho mã nguồn)Hai quy tắc giữ cho cấu trúc này đơn giản:
- Một kho lưu trữ chỉ là một git repo. Bạn commit, push, pull và xem xét nó một cách độc lập. OpenSpec không tự động clone, đồng bộ hóa hoặc đẩy bất kỳ dữ liệu nào.
- Khai báo, không phải cơ chế phức tạp. Các repo có thể khai báo cách chúng liên quan đến các kho lưu trữ (được hiển thị bên dưới). Các khai báo thay đổi những gì OpenSpec có thể cung cấp cho bạn — không bao giờ thay đổi nơi lệnh của bạn hoạt động.
Năm phút để tạo kho lưu trữ đầu tiên của bạn
Hai lệnh đưa bạn từ trạng thái trống sang một thay đổi có phạm vi kho lưu trữ hoạt động:
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Đó là toàn bộ mô hình. Từ đây, vòng đời chính xác là những gì bạn đã biết — status, instructions, validate, archive — với --store team-plans trong mỗi lệnh, và mọi gợi ý được in ra đều mang theo cờ đó cho bạn. Dòng Using OpenSpec root: luôn cho bạn biết lệnh đang hoạt động ở đâu.
Câu chuyện: một nhóm, một repo lập kế hoạch
Một nhóm giữ các đặc tả (specs) và thay đổi của họ trong team-plans thay vì phân tán chúng qua các kho mã nguồn.
Ngày đầu tiên (bất kỳ ai thiết lập):
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 mainViệc truyền --remote ghi lại URL clone vào bên trong tệp định danh của kho (.openspec-store/store.yaml), trong commit ban đầu. Mọi lần clone trong tương lai đều sinh ra với thông tin về nguồn gốc của nó, do đó các kiểm tra sức khỏe và thông báo lỗi có thể in ra một bản sửa chữa đầy đủ, dễ dàng sao chép cho các đồng nghiệp chưa có kho đó.
Mỗi thành viên trong nhóm (một lần trên mỗi máy):
git clone git@github.com:acme/team-plans.git ~/openspec/team-plans
openspec store register ~/openspec/team-plansTừ đó trở đi, mọi người làm việc trong cùng một repo lập kế hoạch theo tên:
openspec status --store team-plans --change add-login
openspec show add-login --store team-plansChia sẻ công việc là git, có chủ đích. Một thay đổi bạn tạo ra chỉ tồn tại trong bản sao cục bộ của bạn cho đến khi bạn commit và push nó — giống như mã nguồn. Các kế hoạch có nhánh, yêu cầu kéo (pull requests) và quá trình xem xét miễn phí, bởi vì một kho lưu trữ là một kho thông thường.
Kết nối các kho mã nguồn của nhóm. Một kho mã nguồn mà việc lập kế hoạch hoàn toàn được tách biệt cần chính xác một dòng, trong openspec/config.yaml:
# web-app/openspec/config.yaml
store: team-plansBây giờ mọi lệnh OpenSpec chạy bên trong web-app sẽ tác động lên team-plans mà không cần bất kỳ cờ nào:
cd ~/src/web-app
openspec status --change add-loginUsing OpenSpec root: team-plans (/Users/you/openspec/team-plans)
...Con trỏ này là phương án dự phòng, không bao giờ là sự ghi đè: một --store rõ ràng luôn chiến thắng, và nếu repo phát triển các thư mục lập kế hoạch thực sự của riêng nó, thì những thứ đó sẽ chiến thắng (với cảnh báo để xóa con trỏ cũ).
Một mặc định cho mọi repo trên máy của bạn. Nếu bạn làm việc trên nhiều kho mã nguồn đều lập kế hoạch vào cùng một kho lưu trữ, hãy đặt nó một lần, ở cấp độ toàn cục, thay vì thêm dòng store: vào mỗi repo:
openspec config set defaultStore team-plansBây giờ bất kỳ lệnh nào chạy bên ngoài một gốc lập kế hoạch — và không có --store cũng như không có con trỏ dự án — sẽ được giải quyết thành team-plans. Nó nằm ở cuối danh sách ưu tiên, vì vậy --store, một gốc cục bộ và con trỏ store: của dự án vẫn luôn chiến thắng. Banner gốc và khối JSON root báo cáo source: "global_default" với id kho lưu trữ, để bạn luôn có thể phân biệt giữa mặc định toàn máy và con trỏ của riêng repo. Xóa nó bằng openspec config unset defaultStore. Nếu id chưa được đăng ký, các lệnh sẽ báo lỗi và bảo bạn đăng ký nó hoặc xóa mặc định cũ.
Ví dụ: một tính năng, hai repo thành phần
Giả sử add-checkout-promo thay đổi cả checkout-api và checkout-web. Nhóm muốn một hợp đồng sản phẩm chung, trong khi mỗi kho mã nguồn vẫn cần các nhiệm vụ triển khai riêng, nhánh và quá trình xem xét riêng.
Sử dụng hai lớp:
- Giữ hành vi dùng chung trong
team-plans. - Giữ các kế hoạch triển khai trong từng repo thành phần và tham chiếu kho lưu trữ như ngữ cảnh upstream chỉ đọc.
Đầu tiên, lên kế hoạch cho hợp đồng dùng chung trong kho:
openspec new change add-checkout-promo --store team-plans
openspec status --change add-checkout-promo --store team-plansĐề xuất và các đặc tả nên mô tả hành vi tại ranh giới giữa các thành phần — ví dụ, các trường khuyến mãi được trả về bởi dịch vụ và cách giao diện người dùng xử lý một giỏ hàng không đủ điều kiện. Xem xét thay đổi này trong kho lưu trữ như bất kỳ nhánh và pull request nào khác.
Ngữ cảnh nào mà việc lập kế hoạch thấy được?
Việc chọn một kho lưu trữ thay đổi gốc OpenSpec; nó không khám phá hay đọc mọi kho mã nguồn sử dụng kho đó. Lệnh của kho lưu trữ thấy các tài liệu và ngữ cảnh đã cấu hình trong kho. Nó chỉ thấy mã thành phần khi các thư mục đó cũng khả dụng cho agent hoặc trình soạn thảo và agent đọc chúng.
Workset là một cách thuận tiện để mở kho lập kế hoạch và cả hai kho mã nguồn cùng lúc:
openspec workset create checkout-promo \
--member ~/openspec/team-plans \
--member ~/src/checkout-api \
--member ~/src/checkout-web \
--tool code
openspec workset open checkout-promoĐiều này làm cho các thư mục hiển thị trong một không gian làm việc IDE duy nhất. Nó không sao chép ngữ cảnh nguồn vào kho, chọn các repo bị ảnh hưởng, hay cấp quyền cho agent chỉnh sửa chúng. Hãy đặt các sự thật xuyên thành phần bền vững vào các đặc tả dùng chung; đừng dựa vào việc người lập kế hoạch nhớ lại nguồn mà tình cờ họ đã kiểm tra.
Việc triển khai bắt đầu trong mỗi repo như thế nào?
Khi không có --store rõ ràng hoặc gốc openspec/ gần hơn áp dụng, con trỏ store: team-plans định tuyến các lệnh đến kho đó. Nó không chia nhỏ danh sách tác vụ của một kho lưu trữ dựa trên thư mục mà lệnh apply được gọi. Hiện tại OpenSpec không định tuyến các tác vụ đến các repo.
Khi mỗi thành phần cần một chu kỳ apply/xem xét có phạm vi độc lập, hãy cung cấp cho nó một gốc OpenSpec cục bộ và tham chiếu đến kho trung tâm thay vì trỏ vào nó:
# checkout-api/openspec/config.yaml (và tương tự trong checkout-web)
schema: spec-driven
references:
- team-plansSau khi hợp đồng dùng chung được phê duyệt và có sẵn trong các đặc tả chính của kho, hãy tạo một thay đổi cục bộ nhỏ cho phần của thành phần:
cd ~/src/checkout-api
openspec new change implement-checkout-promo-api
cd ~/src/checkout-web
openspec new change implement-checkout-promo-uiChỉ số tham chiếu trong hướng dẫn của mỗi repo cung cấp tóm tắt đặc tả của kho và lệnh fetch chính xác (openspec show ... --store team-plans). Mỗi đề xuất cục bộ trích dẫn hợp đồng dùng chung đó, và các nhiệm vụ của nó chỉ mô tả công việc trong thành phần đó. Sau đó, chạy /opsx:apply trong mỗi repo riêng biệt; việc giải quyết gốc giữ cho các tài liệu và chỉnh sửa triển khai có phạm vi trong repo đó. Các thay đổi dịch vụ và giao diện người dùng giờ đây có thể được kiểm thử, xem xét, hợp nhất và lưu trữ một cách độc lập.
Nếu việc triển khai phải bắt đầu trong khi thay đổi kho lưu trữ dùng chung vẫn còn đang hoạt động, hãy fetch nó một cách rõ ràng bằng openspec show add-checkout-promo --store team-plans; các chỉ số tham chiếu liệt kê các đặc tả kho chuẩn, không phải các thay đổi kho đang hoạt động. Hãy giữ liên kết giữa nhánh kho và các nhánh thành phần trong mô tả pull-request của chúng để người xem xét có thể thấy phiên bản hợp đồng nào mà mỗi triển khai đang tuân thủ.
Câu chuyện: các yêu cầu vượt qua ranh giới nhóm
Một nhóm nền tảng sở hữu các yêu cầu. Các nhóm sản phẩm xây dựng dựa trên chúng, trong các repo riêng của họ, với các thiết kế riêng của họ. Một tham chiếu mô tả mối quan hệ đó mà không di chuyển công việc của bất kỳ ai.
platform-reqs (kho lưu trữ) api-server (kho mã nguồn)
do nhóm nền tảng sở hữu do nhóm sản phẩm sở hữu
┌──────────────────────────┐ ┌──────────────────────────┐
│ openspec/specs/ │ ◀────────│ openspec/config.yaml │
│ payments/spec.md │ đọc │ references: │
│ auth/spec.md │ │ - platform-reqs │
│ │ │ openspec/specs/ │
│ openspec/changes/ │ │ (các thiết kế của họ) │
│ công việc nền tảng │ │ openspec/changes/ │
│ │ │ (công việc của họ) │
│ │ └──────────────────────────┘
└──────────────────────────┘Nhóm sản phẩm khai báo những gì họ sử dụng trong openspec/config.yaml của repo họ:
references:
- platform-reqsCác tham chiếu là ngữ cảnh chỉ đọc. Repo giữ gốc openspec/ của riêng mình; công việc vẫn ở đó. Điều thay đổi: openspec instructions trong repo đó bây giờ bao gồm một chỉ mục các đặc tả của kho được tham chiếu — mỗi cái với một tóm tắt một dòng và lệnh fetch chính xác (openspec show <spec-id> --type spec --store platform-reqs). Một agent làm việc trong api-server có thể tìm thấy các yêu cầu thanh toán upstream, trích dẫn chúng, và viết thiết kế chi tiết thấp trong gốc của riêng repo — mà không cần ai đó sao chép ngữ cảnh xung quanh.
Một tham chiếu có thể mang nguồn clone của nó, vì vậy các đồng nghiệp chưa có kho sẽ nhận được một bản sửa chữa đầy đủ thay vì một ngõ cụt:
references:
- { id: platform-reqs, remote: "git@github.com:acme/platform-reqs.git" }Khi bạn muốn mở cả kế hoạch và mã nguồn cùng lúc, hãy tạo một workset. Đây là cá nhân và rõ ràng: mỗi người chọn các thư mục họ thực sự làm việc trên máy của mình. Không có gì về các đường dẫn checkout cục bộ đó được commit vào repo lập kế dùng chung.
openspec workset create platform \
--member ~/openspec/platform-reqs \
--member ~/src/api-server \
--member ~/src/web-appHai câu hỏi bạn luôn có thể hỏi
"Cài đặt của tôi có ổn không?" — openspec doctor kiểm tra gốc hiện tại và các kho lưu trữ được tham chiếu của nó, ở chế độ chỉ đọc, với một bản sửa chữa dễ dàng sao chép cho mỗi phát hiện:
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"Tôi đang làm việc với những gì?" — openspec context tập hợp bộ làm việc từ các khai báo OpenSpec: gốc và các kho lưu trữ mà nó tham chiếu.
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-reqsCả hai đều hỗ trợ --json cho các agent. openspec context --code-workspace <path> additionally writes a VS Code workspace file containing the whole set — the only write this command performs.
Worksets: mở lại các thư mục bạn làm việc cùng nhau
Tách biệt với tất cả những nội dung trên: hầu hết mọi người đều mở cùng một vài thư mục trong mỗi phiên làm việc — repo kế hoạch cộng với hai hoặc ba repo mã nguồn. Một workset (bộ công việc) là một chế độ xem cá nhân, có tên, chính xác cho trường hợp đó, được mở lại chỉ bằng một lệnh trong công cụ bạn chọn.
workset "platform" openspec workset open platform
├── team-plans ~/openspec/team-plans │
├── api-server ~/src/api-server ▼
└── web-app ~/src/web-app cả ba đều mở trong công cụ của bạnopenspec workset create platform \
--member ~/openspec/team-plans --member ~/src/api-server \
--tool code
openspec workset listplatform (mở trong VS Code)
team-plans /Users/you/openspec/team-plans
api-server /Users/you/src/api-serveropenspec workset open platform sau đó sẽ khởi chạy công cụ đã lưu: các trình soạn thảo (VS Code, Cursor) sẽ mở một cửa sổ duy nhất chứa tất cả các thành viên rồi kết thúc. Thành viên đầu tiên được coi là thành viên chính. Bạn có thể ghi đè công cụ bất cứ lúc nào bằng cách sử dụng --tool <id>.
Worksets cố ý không phải là trạng thái chia sẻ. Chúng tồn tại trên máy của bạn, không bao giờ được commit và không đưa ra tuyên bố gì về công việc — chúng chỉ ghi lại những gì bạn thích mở cùng nhau. Việc xóa một workset không bao giờ ảnh hưởng đến các thư mục thành viên. Các công cụ mới là cấu hình, không phải mã nguồn: bất kỳ thứ gì được khởi chạy thông qua tệp workspace hoặc các cờ đính kèm theo từng thư mục đều có thể được thêm vào khóa openers trong cấu hình toàn cục (openspec config edit).
Cách các lệnh quyết định hành động ở đâu
Mỗi lệnh bình thường đều xác định gốc (root) của nó theo cùng một cách, theo thứ tự sau:
1. --store <id> bạn nói rõ ràng → store đó
2. nearest openspec/ một gốc kế hoạch thực sự → repo này
(duyệt lên từ cwd)
3. store: pointer config.yaml khai báo một → store đó
store
4. defaultStore cấu hình toàn cục đặt một → store đó
mặc định cho máy
5. none of the above có stores được đăng ký → lỗi với gợi ý
trên máy này? lựa chọn
không có stores nào → thư mục hiện tại
được đăng ký? (hành vi cổ điển)Dòng Using OpenSpec root: (và khối root trong đầu ra --json) cho bạn biết bạn đang rơi vào trường hợp nào.
Các hạn chế đã biết
- Hình dạng Beta. Mọi thứ trên trang này có thể thay đổi giữa các bản phát hành — tên, cờ, định dạng tệp, khóa JSON.
- Một bản sao chép (checkout) cho mỗi id store trên mỗi máy. Việc đăng ký một bản sao chép thứ hai dưới cùng một id sẽ thất bại với gợi ý hãy chạy
store unregistertrước. - Không đồng bộ hóa, chưa bao giờ — do thiết kế. OpenSpec không bao giờ clone, pull hay push. Một bản sao chép cũ sẽ hiển thị các đặc tả cũ cho đến khi bạn pull; các tham chiếu được lập chỉ mục trực tiếp từ những gì có trên đĩa.
- Các thư mục kế hoạch trống có thể không tồn tại. Một store mới có thể chưa có
openspec/changes/,openspec/specs/, hoặcopenspec/changes/archive/trong Git. Điều này được chấp nhận trong giai đoạn beta; các thư mục đó sẽ xuất hiện khi các lệnh bình thường tạo tệp cho chúng. - Các repo con trỏ vẫn là con trỏ. Một repo chỉ có cấu hình mà
openspec/config.yamlkhai báostore: <id>được coi là kế hoạch hóa bên ngoài, không phải là một bản sao chép store để đăng ký. Xóa dòngstore:trước nếu bạn muốn chuyển đổi repo đó thành một gốc store cục bộ một cách có chủ đích. - Một số lệnh vẫn giữ nguyên vị trí.
templatesvà các dạng danh từ đã lỗi thời (openspec change show, ...) chỉ hoạt động trên thư mục hiện tại — không có--store.schemastuân theo thứ tự ưu tiên chọn gốc chuẩn và chấp nhận--store <id>trong khi vẫn giữ nguyên định dạng mảng JSON thành công. - Trạng thái theo máy là theo máy. Đăng ký store và worksets là các cài đặt cục bộ. Không có gì về bố cục máy của bạn bao giờ được commit vào kế hoạch chia sẻ.
- Hai kiểu khởi chạy cho worksets. Một công cụ không thể khởi chạy bằng tệp workspace hoặc các cờ đính kèm theo từng thư mục thì không thể được thêm vào như một opener.
- Agent JSON có một sự phân tách chữ hoa/chữ thường đã biết (các khóa họ hàng store là snake_case, họ hàng workflow là camelCase). Được tài liệu hóa trong hợp đồng agent; việc thống nhất nó được trì hoãn đến một bản phát hành có phiên bản.
Vị trí của các thành phần
| Cái gì | Ở đâu | Chia sẻ? |
|---|---|---|
| Kế hoạch của một store | <store>/openspec/ (specs, changes) | Có — commit và push nó |
| Danh tính của một store | <store>/.openspec-store/store.yaml | Có — được commit cùng với store |
| Đăng ký store | <data dir>/openspec/stores/registry.yaml | Không — chỉ máy này |
| Worksets | <data dir>/openspec/worksets/ | Không — chỉ máy này |
<data dir> là ~/.local/share/openspec trên macOS và Linux (hoặc $XDG_DATA_HOME/openspec khi được đặt), và %LOCALAPPDATA%\openspec trên Windows.
Tham khảo
Chính xác các cờ và định dạng JSON cho mọi lệnh trên trang này: Tài liệu tham khảo CLI (Stores, Doctor, Working context, Personal worksets) và hợp đồng agent.