AIDive

5 Quy Tắc Giữ Spec Superpowers Khỏi Mục Nát Trong Repo

Bởi AIDive · Đăng ngày

Coding agent

Mười lập trình viên, một thư mục docs

Superpowers là một plugin của Claude Code (286.000 star trên GitHub) với các skill viết spec và implementation plan dưới dạng file markdown rồi commit chúng vào repository. Theo mặc định, mọi file như vậy đều đổ vào cùng một thư mục, docs/superpowers/. Vào tháng 6, các maintainer của Okama, một thư viện tài chính Python, phát hiện ra tám implementation plan mà một agent đã viết vào đó, commit đúng như plugin dự tính, đã bị render thành các trang web công khai trên Read the Docs. Họ chỉ nhận ra sau khi bản release đã lên.

Giờ hãy nhân quy mô đó lên: một team front-end mười lập trình viên, bốn squad, một repository, và mọi lập trình viên đều đang tạo ra các file này. Ba câu hỏi rơi lên bàn của lead. Chúng ta version cái gì? Drift có phải là vấn đề khi một spec nhắc tới một đường dẫn đã bị xoá từ hai sprint trước? Và ai quản trị thư mục đó? Vụ rò rỉ của Okama không phải do việc commit gây ra. Nó xảy ra do commit mà không có quy tắc. Bài viết này đưa ra cho bạn năm quy tắc.

Mọi công cụ đều commit các tệp

Superpowers không bao giờ hỏi có nên commit hay không. Dòng 18 của skill lập plan lưu mọi plan vào docs/superpowers/plans/, có ngày, một file cho mỗi feature. Skill brainstorming viết design document và commit nó ngay trong cùng bước, trước khi bạn kịp xem plan.

Đây không phải là một điểm lạ riêng của Superpowers. Mọi công cụ spec-driven lớn đều chọn cùng một cách:

Tool Publisher Where specs live
Superpowers (286,000 stars) obra docs/superpowers/, một thư mục cho tất cả
Spec Kit (136,000 stars) GitHub một thư mục đánh số cho mỗi feature, trên branch của feature đó
Kiro Amazon .kiro/specs/, một thư mục cho mỗi feature
AI-native SDLC playbook Anthropic một artifact được commit cho mỗi giai đoạn: intent, spec, plan, diff, review findings, incident record

Kiro giới thiệu các thư mục theo từng feature như một cách để các đồng nghiệp cùng cộng tác trên nhiều feature khác nhau đồng thời. Playbook của Anthropic, công bố vào tháng 8, đi xa hơn: mỗi giai đoạn commit một artifact mà giai đoạn kế tiếp có thể đọc. Vậy câu hỏi không bao giờ là có nên commit hay không, và câu hỏi lưu ở đâu thì công cụ nào cũng trả lời được. Ai sở hữu file, và khi nào nó đã hết hạn, thì không. Đó chính là khoảng trống mà năm quy tắc này lấp vào.

Ba cách nó đi sai hướng

Các plan đã commit thất bại theo ba cách khác nhau.

Thất bại 1: docs generator. Một bản build documentation render mọi file markdown dưới docs/, bất kể có được liệt trong table of contents hay không, sẽ đem cả thư mục plan đi cùng với manual. Đó chính xác là điều đã xảy ra với Okama.

Thất bại 2: branch. Issue 1246 của Superpowers ghi nhận rằng brainstorm commit plan thẳng vào main. Một người dùng đếm được 10 đến 15 commit mỗi session, một commit mới sau vài thay đổi nhỏ. Yêu cầu của thread đó gói trong một dòng: không được có plan và spec trên nhánh main.

Thất bại 3: drift. Spec drift là thất bại âm thầm nhất: code tiến triển còn spec thì không. Birgitta Böckeler tại Thoughtworks đặt tên cho ba cấp độ của spec-driven development:

Level Meaning
Spec-first Được viết ra, dùng một lần
Spec-anchored Được giữ và bảo trì suốt đời của feature
Spec-as-source Con người chỉ chỉnh sửa spec

Superpowers viết các document kiểu spec-first và giữ chúng vĩnh viễn. Theo mặc định bạn có một kiểu lưu trữ spec-anchored nhưng lại được bảo trì như bản nháp đầu: không ai cập nhật file, và một agent đọc nó vào quý sau như thể đó là sự thật. TrueFoundry nói thẳng: drift là không thể tránh trong thực hành spec-first và được quản lý trong thực hành spec-anchored, nhưng chỉ khi sự sai lệch đó có thể phát hiện được.

Một repository chứa 41 spec và 9 agent file không có vấn đề về gọn gàng. Nó có 41 behavior input chưa được version. Phản ứng của chính Böckeler: bà thích review code hơn là đọc hết đám file markdown này. Một plan mà không ai đọc lại là rác. Một plan mà agent đọc là một instruction, và một instruction cũ là một instruction sai.

Quy tắc 1: mỗi squad một nơi ở, có chủ

Quy tắc 1: thư mục phải có chủ, và chủ đó là squad, không phải plugin. Từ phiên bản 5, Superpowers ưu tiên các instruction trong CLAUDE.md của project hơn mặc định của chính nó. Nói rõ plan lưu ở đâu và chúng sẽ nằm ở đó.

Tuy vậy, một bảng đường dẫn thôi là chưa đủ. Trong issue 939, một người dùng viết một bảng output-path và model vẫn theo đường dẫn cụ thể trong skill và bỏ qua bảng đó. Ghi chú override của chính skill chỉ là một dòng trong ngoặc. Một dòng mệnh lệnh, in đậm, đã sửa được điều này ngay lần đầu: spec phải được lưu ở đây, không phải ở kia, với đường dẫn mặc định được nêu rõ như thứ cần tránh.

Cách bố trí là một thư mục cho mỗi squad (checkout, search, accounts, design-system), mỗi thư mục có spec và plan riêng của nó. Trong một monorepo, đặt dòng đó vào CLAUDE.md của riêng package. Claude Code nạp file của một subdirectory theo yêu cầu khi nó đọc ở đó, và documentation nói rằng chủ của mỗi thư mục thường là người bảo trì file đó. Các engineer chưa từng cài plugin không bị ảnh hưởng: dòng đó chỉ kích hoạt khi một skill hỏi nơi lưu.

Sau đó hãy để platform thực thi quyền sở hữu. Một dòng CODEOWNERS cho mỗi thư mục squad, và mọi spec review sẽ đến đúng những người sẽ phải sống cùng nó. Một lưu ý: override chỉ là một prompt, không phải một setting. Kiểm tra spec đầu tiên sau mỗi lần plugin release.

Quy tắc 2: cá nhân và dùng chung

Quy tắc 2: không phải plan nào cũng là artifact của team. Người đầu tiên yêu cầu Superpowers cho một vị trí lưu có thể cấu hình (issue 337) đã nói đúng nhất: file plan có thể là tài liệu làm việc cá nhân hơn là artifact của project.

Phía cá nhân có một file dành riêng cho nó. CLAUDE.local.md nằm cạnh file dùng chung, nạp sau nó, và documentation bảo bạn tự git-ignore nó. Đường dẫn riêng của bạn tới một thư mục cá nhân nằm ở đó và không ai khác thấy được. Đối với thư mục tự thân, Git có một ignore file không bao giờ chạm vào tree dùng chung: .git/info/exclude chỉ tồn tại per clone, không bao giờ được commit, và không cần pull request. Chính plugin đã làm điều này cho không gian scratch của nó: từ phiên bản 6.0.3, working directory của nó tự thả một .gitignore chứa * bên trong, nên nó tự ignore chính nó mà không đụng tới bất kỳ tracked file nào.

Phía dùng chung là bất cứ thứ gì đã qua được review gate mà brainstorm đã chạy sẵn: spec được viết, review được yêu cầu, được một người thứ hai approve. Được approve, nó chuyển vào thư mục squad. Không được approve, nó ở lại cục bộ. Cả hai đều được viết trên một branch, không bao giờ trên main. Hai dòng trong CLAUDE.md dùng chung làm được điều đó: coi spec và plan là điểm bắt đầu của một feature, và tạo worktree trước khi viết chúng.

Một giới hạn: một plan bị git-ignore không di chuyển được. Các maintainer của Okama đã thử ignore cả thư mục trước, rồi phải đảo ngược, vì các plan ngừng sync giữa các máy và các agent. Cá nhân nghĩa là cá nhân.

Quy tắc 3: một vòng đời, như ADR

Quy tắc 3: một spec có một status, và một spec đã chết phải nói rõ điều đó. Ý tưởng này đã 15 năm tuổi. Michael Nygard, vào năm 2011, đề xuất giữ các architecture decision record (ADR) trong repository, mỗi bản một file được đánh số. Một quyết định được đề xuất, rồi được chấp thuận. Khi một bản ghi sau đó thay đổi nó, bản cũ được đánh dấu deprecated hoặc superseded kèm tham chiếu tới bản thay thế. Nếu một quyết định bị đảo ngược, bản ghi cũ vẫn được giữ nhưng đánh dấu superseded.

Áp dụng vào spec, mỗi spec dùng chung mở đầu bằng một header năm dòng mà agent vẫn đọc:

Field Purpose
status proposed, accepted, superseded
superseded-by spec thay thế
owner squad
touches các đường dẫn spec đưa ra tuyên bố về
review date lần cuối được kiểm tra

Không bao giờ chỉnh sửa một spec đã accepted thành một quyết định khác. Viết bản kế tiếp, chỉ ngược về bản cũ, và lịch sử vẫn trung thực. Một ngoại lệ, được nêu trong playbook của Anthropic: khi implementation lệch khỏi plan, cập nhật plan trong cùng commit đó. Plan đi cùng với diff đã làm nó lệch, và một hook có thể thực thi điều này.

Spec Kit gọi tên ba cách một spec có thể sống. Flow-forward: một thư mục mới cho mỗi feature, các thư mục cũ được giữ như lịch sử. Living spec: spec là hợp đồng và phần còn lại được sinh lại từ nó. Flow-back: bản build định hình lại spec. Quy tắc của họ là không để một thay đổi trong task hay code nói khác với điều spec vẫn đang nói. Đó là chọn spec-anchored một cách có chủ đích.

Giới hạn: status là metadata do con người đặt. Agent sẽ không tự đánh dấu spec của chính nó là superseded trừ khi CLAUDE.md của bạn bảo nó làm vậy, và ngay cả khi đó review vẫn phải để ý.

Quy tắc 4: một drift gate trong CI

Quy tắc 4: một spec nói dối về tree sẽ làm build fail. Bài paper đặt tên cho vấn đề này, công bố vào tháng 6, gọi nó là silent spec-code drift: code tiến triển, specification không, và sự sai lệch vẫn vô hình cho tới khi việc sửa nó trở nên đắt đỏ. Câu trả lời của họ là một drift gate, một điều kiện merge có tính chặn.

Việc kiểm tra nhỏ hơn nghe có vẻ. Với mỗi spec mà header ghi accepted, lấy các đường dẫn nó nêu, trong backtick hoặc trong dòng touches, và kiểm tra từng đường dẫn có tồn tại. Đường dẫn thiếu, build fail, kèm tên spec trong log. Các file superseded và proposed được bỏ qua: chỉ spec accepted mới đưa ra tuyên bố về tree, nên chỉ spec accepted mới bị kiểm tra. TrueFoundry gọi đây là một diff theo lịch trình thay vì khảo cổ sau sự cố: một spec là scaffold, và một thay đổi spec là một thay đổi chính sách.

Người ta đã chạy phiên bản thủ công của việc này. Một người bình luận cho Claude so sánh các file spec với codebase và mở ticket cho những gì còn thiếu. Script làm điều đó trên mỗi pull request, miễn phí.

Lớp bảo vệ thứ hai là điều Okama đã triển khai: loại thư mục đó ra khỏi docs build. Một dòng trong cấu hình Sphinx giữ các file trong Git nhưng ngoài HTML.

Giới hạn: kiểm tra đường dẫn chỉ bắt được file đã xoá, không bắt được hành vi đã thay đổi. Một spec có thể nêu tên mọi file đang tồn tại và vẫn mô tả một API đã không còn nữa. Đó là việc mà review và quy tắc same-commit đảm nhiệm.

Quy tắc 5: một ngân sách kích cỡ, và cái giá

Quy tắc 5: hầu hết công việc không đáng có một spec. Bằng chứng về chi phí phụ trội của spec-driven là nhất quán:

Experiment Result
Marmelab, Spec Kit trên một feature chỉ hiển thị ngày hiện tại 8 file, 1.300 dòng văn bản spec
Cuộc bake-off của OpenSpec, cùng yêu cầu với một spec tool so với Claude Code một mình nhiều hơn 50% code, nhiều hơn 50% cyclomatic complexity, gấp đôi thời gian, gấp ba chi phí
Một team chạy spec-driven development trong nhiều tháng 2 đến 3 lần số token, gấp khoảng đôi thời gian, chi phí điều phối lớn cho những thay đổi không cần điều đó; không có bằng chứng code tốt hơn

Bake-off chỉ là một thí nghiệm, không phải một benchmark, và tác giả của nó tự nói vậy. Nhưng xu hướng vẫn đúng. Vậy quy tắc là: viết một spec khi công việc vượt qua ranh giới một squad hoặc sẽ được đọc lại trong 90 ngày. Mọi thứ khác chỉ là một prompt.

Superpowers đã tự phân loại mọi request vào ba nhánh: spike, bounded, architectural. Chỉ nhánh architectural mới viết spec. Giữ nhánh bounded thật gọn, và giữ spec quanh 300 dòng. Người thực hành thêm hai dòng nữa: đừng viết spec quá lớn, và hãy đọc các spec đã được sinh ra.

Giới hạn này đi cả hai chiều. Cùng cuộc bake-off đó tìm ra ba lỗ hổng mà bản chạy thường bỏ lỡ. Spec mua được coverage, không mua được tốc độ. Trả tiền cho nó ở nơi coverage thực sự quan trọng.

Kết luận: commit, rồi quản trị

Trở lại ba câu hỏi của lead. Version cái gì: các spec và plan đã accepted, trên một branch, trong thư mục của squad. Drift: một header status và một CI gate. Governance: chủ sở hữu và một quy tắc kích cỡ.

Năm quy tắc, và không cái nào được một công cụ thực thi hôm nay. Issue về override vẫn đang mở. Issue về main branch vẫn đang mở. Pull request sắp xếp lại instruction đã bị đóng mà không được merge. Team đã bị bỏng vẫn giữ thực hành này: 8 plan và 6 spec, đã commit, loại khỏi docs build.

Phản đối của Böckeler vẫn đúng. Với một header status và một ngân sách kích cỡ, bạn chỉ đọc các spec đã accepted, và ít hơn trong số đó. Điều bạn nhận lại là điều playbook của Anthropic gọi là audit trail: ai đã yêu cầu điều gì, agent đã tạo ra gì, và ai đã approve.

Đây là quy trình, không phải tooling: một file CLAUDE.md, một file CODEOWNERS, một header, và một script mười lăm dòng. Và nếu team không chịu review một yêu cầu CODEOWNERS, họ cũng sẽ không review một spec. Trong trường hợp đó, .gitignore mới là lựa chọn trung thực.

Nguồn

Câu hỏi thường gặp

Có nên commit các spec và plan của Superpowers vào Git không?
Có, nhưng phải có quy tắc. Mọi công cụ spec-driven (Superpowers, Spec Kit, Kiro, playbook của Anthropic) đều commit chúng theo mặc định. Commit các spec đã được approve trên một feature branch, trong một thư mục do squad sở hữu, giữ bản nháp cá nhân ngoài tree dùng chung, và loại thư mục đó khỏi docs build.
Làm sao để đổi nơi Superpowers lưu plan?
Từ phiên bản 5, plugin ưu tiên CLAUDE.md của project hơn mặc định của chính nó. Dùng một dòng mệnh lệnh in đậm nêu rõ đường dẫn mới và đường dẫn mặc định cần tránh; một bảng output path đã bị model bỏ qua trong issue 939. Kiểm tra lại spec đầu tiên sau mỗi lần plugin release, vì override chỉ là một prompt, không phải một setting.
Spec drift trong spec-driven development là gì?
Spec drift là khi code tiến triển còn spec đã commit thì không, khiến một agent đọc lại spec sau đó nhận một instruction sai. Thoughtworks phân biệt spec-first (dùng một lần), spec-anchored (được bảo trì) và spec-as-source; Superpowers viết file kiểu spec-first và giữ chúng vĩnh viễn, điều này tạo ra drift theo mặc định.
Làm sao để phát hiện spec drift trong CI?
Thêm một drift gate: với mỗi spec mà header ghi accepted, trích các đường dẫn nó nêu và làm build fail nếu đường dẫn nào không còn tồn tại. Đây chỉ là khoảng mười lăm dòng script và chạy trên mỗi pull request. Nó chỉ bắt được file đã xoá, không bắt được hành vi đã thay đổi, nên cần kết hợp với review và một quy tắc cập nhật same-commit.
Nên version các spec AI như thế nào, giống ADR?
Cho mỗi spec dùng chung một header gồm status, superseded-by, owner, các đường dẫn liên quan và ngày review. Không bao giờ chỉnh sửa một spec đã accepted thành một quyết định khác: viết bản kế tiếp và chỉ ngược về bản cũ, giống cách architecture decision record của Michael Nygard hoạt động. Ngoại lệ duy nhất là cập nhật plan trong cùng commit khi nó lệch khỏi kế hoạch.
Spec-driven development có đáng chi phí không?
Với hầu hết công việc thì không. Cuộc bake-off của OpenSpec tạo ra nhiều hơn 50% code và độ phức tạp, tốn gấp đôi thời gian và gấp ba chi phí so với dùng Claude Code một mình, dù bắt được ba lỗ hổng mà bản chạy thường bỏ lỡ. Chỉ dành spec cho công việc vượt ra ngoài một squad hoặc sẽ được đọc lại trong 90 ngày, và giữ chúng quanh 300 dòng.

Video liên quan