Przejdź do głównej zawartości

OpenSpec: propozycje zmian i żywe specyfikacje

OpenSpec to otwartoźródłowa warstwa specyfikacji od Fission AI dla agentów kodujących, które pracują na istniejącym kodzie. Każda zmiana dostaje propozycję, delty wymagań i listę zadań; gdy agent ją zaimplementuje, archiwizacja scala delty w openspec/specs/, żywy opis tego, jak system działa teraz. Działa w Claude Code, Codex i Cursorze.

Endpoint resetu hasła ma sześć lat, nikt nie spisał, jak się zachowuje, a dział bezpieczeństwa chce w tym sprincie limitu żądań, a w następnym krótszego czasu życia tokenu. Możesz dać agentowi ticket i przejrzeć diff albo uruchomić Spec Kit i wyprodukować specyfikację, plan, notatki z researchu i kontrakty dla dwugodzinnej zmiany. Żadna z tych dróg nie zostawia ci spisanej, aktualnej odpowiedzi na pytanie „co dziś robi reset hasła?”.

Ta strona jest dla programisty, który uruchamia agenta na kodzie z historią. Przeprowadza obie te zmiany przez OpenSpec, pokazuje dokładne pliki i polecenia, a na końcu daje regułę, kiedy lepszym wyborem jest Spec Kit.

Co zyskujesz, uruchamiając OpenSpec na istniejącym kodzie

Dział zatytułowany „Co zyskujesz, uruchamiając OpenSpec na istniejącym kodzie”
  • Działającą instalację dla Claude Code, Codex i Cursora w jednym repozytorium oraz pisownię poleceń w każdym narzędziu.
  • Regułę, która rozstrzyga o pierwszej zmianie w kodzie bez specyfikacji: tylko wymagania ADDED mogą utworzyć specyfikację, i dlatego testy charakteryzacyjne są krokiem zerowym.
  • Dwie prawdziwe delty dla endpointu resetu hasła: jedną, która tworzy specyfikację, i drugą, która modyfikuje i usuwa w niej wymagania.
  • Prompty do skopiowania dla explore, propose, apply i pull requesta.
  • Job CI, który odrzuca źle sformatowaną zmianę i archiwizację, która zapomniała zaktualizować żywą specyfikację.
  • Tabelę decyzyjną: OpenSpec czy Spec Kit, gdy kod już istnieje.

OpenSpec rozdziela dwie rzeczy: to, co system robi teraz (openspec/specs/), i to, co zmieni jedna zmiana (openspec/changes/<nazwa>/). Tak wygląda drzewo po zarchiwizowaniu pierwszej zmiany z tego tutorialu i zaproponowaniu drugiej:

  • Folderopenspec/
    • config.yaml schemat spec-driven, opcjonalne context: i rules:
    • Folderspecs/
      • password-reset/spec.md żywa specyfikacja, zapisywana wyłącznie przez archive
    • Folderchanges/
      • Foldershorten-reset-token-ttl/ w toku
        • proposal.md dlaczego, co się zmienia, których obszarów dotyczy
        • specs/password-reset/spec.md delta
        • design.md opcjonalny, dla zmian przekrojowych albo ryzykownych
        • tasks.md numerowane checkboksy, przez które przechodzi apply
      • Folderarchive/
        • Folder2026-09-26-add-reset-rate-limit/ pierwsza zmiana, zachowana jako historia
          • …

Plik delty używa czterech nagłówków sekcji. Krok archive nakłada je na odpowiadający plik w openspec/specs/ i przy każdym jest rygorystyczny:

Sekcja deltyDo czego służyCo robi archiveCo ją wykoleja
## ADDED RequirementsNowe zachowanie albo istniejące zachowanie, które spisujesz po raz pierwszyDopisuje wymaganie; tworzy plik specyfikacji, jeśli go nie maWymaganie, którego nagłówek już istnieje w specyfikacji z inną treścią (nawet jeśli różni się tylko wielkością liter lub spacjami); do tego służy MODIFIED. ADDED to jedyna operacja, która może utworzyć specyfikację
## MODIFIED RequirementsZmienione zachowanieZastępuje cały blok wymagania twoimOdmawia, gdy plik specyfikacji nie istnieje, gdy nagłówek się nie zgadza albo gdy twój blok gubi scenariusz, który ma aktualna specyfikacja
## REMOVED RequirementsZachowanie, które wycofujesz, z liniami **Reason** i **Migration**Usuwa wymaganieNa specyfikacji, która jeszcze nie istnieje, ignoruje usunięcie z ostrzeżeniem
## RENAMED RequirementsSama zmiana nazwy, w formie FROM: / TO:Zmienia nagłówekBlok MODIFIED, który używa starej nazwy

W każdej sekcji wymaganie to ### Requirement: <nazwa> z tekstem normatywnym (SHALL albo MUST), a pod nim co najmniej jeden #### Scenario: <nazwa> z liniami WHEN i THEN. Scenariusz ma dokładnie cztery krzyżyki; kolejne sekcje pokazują, co się dzieje, gdy ich brakuje.

Wartość tego modelu leży w scalaniu. Za pół roku openspec/specs/password-reset/spec.md odpowie na pytanie „co robi reset hasła?”, a każdy zarchiwizowany katalog zmiany na pytanie „kiedy i dlaczego to się zmieniło?”. Narzędzie ze specyfikacją na funkcję daje ci tylko to drugie.

OpenSpec wymaga Node.js 20.19.0 lub nowszego (engines w pakiecie 1.13.2). Pakiet npm to @fission-ai/openspec, a polecenie to openspec.

  1. Zainstaluj CLI raz na maszynę (terminal):

    Okno terminala
    npm install -g @fission-ai/openspec@latest
    openspec --version # oczekuj 1.13.2 lub nowszej

    README wymienia też oficjalną formułę Homebrew: brew install openspec. CLI wysyła anonimowe statystyki użycia (według README nazwy poleceń i wersję) i sam je wyłącza w CI; żeby zrezygnować na swojej maszynie, ustaw OPENSPEC_TELEMETRY=0 albo uruchom openspec config set telemetry.enabled false.

  2. Zainicjuj repozytorium dla agentów, których używa zespół. Jeden init obsłuży wszystkie trzy:

    Okno terminala
    cd twoje-repo
    openspec init --tools claude,codex,cursor

    Zapisuje sześć skilli w .claude/skills/openspec-*/ i sześć poleceń w .claude/commands/opsx/: explore, propose, apply, archive, sync i update. W sesji wpisujesz /opsx:propose. Polecenia mają allowed-tools: Bash(openspec:*), więc agent może wołać CLI bez pytania o zgodę; nic więcej nie jest wstępnie dozwolone.

  3. Opisz projekt agentowi raz, w openspec/config.yaml. Pole context: jest czytane przed każdą propozycją, a rules: dodają ograniczenia dla poszczególnych artefaktów:

    schema: spec-driven
    context: |
    Express 4 API in TypeScript, Postgres through Knex, Vitest + supertest.
    Password reset lives in src/routes/auth/password-reset.ts.
    rules:
    tasks:
    - Every task names the test that proves it
    proposal:
    - Always include a Non-goals section
  4. Zacommituj openspec/, katalogi skilli i katalogi poleceń, żeby każdy członek zespołu i każdy agent czytał ten sam workflow.

Przeprowadź pierwszą zmianę w kodzie bez specyfikacji

Dział zatytułowany „Przeprowadź pierwszą zmianę w kodzie bez specyfikacji”

Pierwsza zmiana dotyczy obszaru funkcjonalnego (w OpenSpec: capability), czyli resetu hasła, który nie ma pliku w openspec/specs/. To przesądza o tym, jak ją zapisać: archive odrzuca deltę MODIFIED wobec nieistniejącej specyfikacji, a deltę REMOVED pomija, wypisując jedynie ostrzeżenie. Dlatego pierwsza zmiana opisuje reset hasła wyłącznie jako wymagania ADDED: dodawany limit żądań oraz istniejące zachowanie, które chcesz spisać i chronić.

  1. Przypnij obecne zachowanie testami, zanim agent czegokolwiek dotknie. Wszystko, co zapiszesz w specyfikacji jako „istniejące”, potrzebuje testu, który dowodzi, że jest prawdą dziś. Napisz testy charakteryzacyjne dla endpointu resetu: odpowiedź dla nieznanego adresu e-mail, wygasanie tokenu i jednorazowe użycie. Zacommituj je osobno.

  2. Explore. explore czyta kod i zadaje pytania; jego skill zabrania pisania kodu.

    Każde zachowanie oznaczone UNTESTED wraca do kroku 1, zanim złożysz propozycję, albo zostaje poza specyfikacją.

  3. Propose. Jedno polecenie tworzy katalog zmiany i wszystkie jej artefakty, po czym się zatrzymuje. Skill propose stwierdza, że żądanie „authorizes planning only”, więc agent nie zacznie implementacji w tej samej turze.

  4. Recenzuj deltę, nie prozę. Otwórz openspec/changes/add-reset-rate-limit/specs/password-reset/spec.md. Fragment tego, co powinieneś zaakceptować:

    ## Purpose
    Lets users regain access to their account by email without revealing which addresses have accounts.
    ## ADDED Requirements
    ### Requirement: Reset request rate limit
    The system SHALL accept at most 5 reset requests per email address per rolling hour.
    #### Scenario: Sixth request within an hour
    - **WHEN** a client sends a 6th reset request for alice@example.com within 60 minutes
    - **THEN** the system responds 429 with a Retry-After header and sends no email
    ### Requirement: Reset token lifetime
    The system SHALL reject a reset token 24 hours after it was issued.
    #### Scenario: Token used after 25 hours
    - **WHEN** a user submits a token issued 25 hours earlier
    - **THEN** the system rejects it and the password is unchanged

    Zaakceptuj ją, gdy każdy scenariusz ma konkretne wejście i sprawdzalne wyjście, gdy każde „istniejące” wymaganie odpowiada przechodzącemu testowi charakteryzacyjnemu i gdy tasks.md przy każdym zadaniu wskazuje test. W schemacie spec-driven plik design.md jest opcjonalny; przy limicie żądań, który wymaga wspólnego licznika dla wielu instancji, poproś o niego.

  5. Zwaliduj strukturę (terminal):

    Okno terminala
    openspec validate add-reset-rate-limit --strict
    openspec show add-reset-rate-limit
  6. Apply. /opsx:apply przechodzi przez tasks.md po kolei, odhacza każde ukończone zadanie jako - [x] i zatrzymuje się, gdy zadanie jest niejasne albo zablokowane. Prompt w następnej sekcji dokłada bramkę testów.

  7. Archive. /opsx:archive sprawdza artefakty i zadania, nakłada deltę i przenosi zmianę do openspec/changes/archive/2026-09-26-add-reset-rate-limit/. Ponieważ ten obszar nie miał specyfikacji, archive tworzy openspec/specs/password-reset/spec.md z wymagań ADDED i sekcji ## Purpose. Z terminala ten sam krok to openspec archive add-reset-rate-limit --yes.

Zmień wyspecyfikowane zachowanie deltami MODIFIED i REMOVED

Dział zatytułowany „Zmień wyspecyfikowane zachowanie deltami MODIFIED i REMOVED”

Sprint później dział bezpieczeństwa prosi o 30-minutowy czas życia tokenu i koniec starego linku z tokenem w query stringu. Teraz openspec/specs/password-reset/spec.md istnieje, więc delta może w niej modyfikować i usuwać wymagania.

Delta, którą widzi recenzent:

## MODIFIED Requirements
### Requirement: Reset token lifetime
The system SHALL reject a reset token 30 minutes after it was issued.
#### Scenario: Token used after 25 hours
- **WHEN** a user submits a token issued 25 hours earlier
- **THEN** the system rejects it and the password is unchanged
#### Scenario: Token used after 31 minutes
- **WHEN** a user submits a token issued 31 minutes earlier
- **THEN** the system rejects it and the password is unchanged
## REMOVED Requirements
### Requirement: Legacy reset link
**Reason**: Tokens in query strings leak through referrer headers and proxy logs.
**Migration**: Emails sent since the 2024 template change use /reset/<token>; users with an older email request a new reset.

Scenariusz „25 hours” wciąż tu jest, choć nowy go obejmuje. To celowe: MODIFIED zastępuje cały blok, a w 1.13.2 zarówno openspec validate, jak i archive odrzucają blok MODIFIED, który gubi scenariusz obecny w aktualnej specyfikacji. Jeśli chcesz usunąć scenariusz, zmień żywą specyfikację w osobnej, zrecenzowanej zmianie, a nie pomijając go w delcie.

Zanim uruchomisz apply, obejrzyj zmianę tak, jak zobaczy ją recenzent (terminal):

Okno terminala
openspec show shorten-reset-token-ttl --type change --diff

--diff wypisuje diff każdej delty względem aktualnej specyfikacji, wymaganie po wymaganiu. To najszybszy sposób, żeby potwierdzić, że zmieniają się tylko linie, o które prosił dział bezpieczeństwa.

Skill apply odhacza zadania; niczego nie dowodzi. Uczyń zestaw testów bramką między zadaniami i nie pozwól agentowi edytować specyfikacji, żeby jego kod przeszedł.

/opsx:apply shorten-reset-token-ttl

Uruchom to w sesji, która złożyła propozycję, żeby agent zachował kontekst. Jeśli zadanie pójdzie źle, /rewind (albo Esc Esc) przywraca sesję do ostatniego checkpointu. Żeby wymusić zasadę „nigdy nie edytuj specyfikacji”, uruchom sesję implementacyjną z regułą deny dla żywych specyfikacji:

Okno terminala
claude --disallowedTools "Edit(/openspec/specs/**)"

Reguła Edit obejmuje wszystkie wbudowane narzędzia edytujące pliki, a początkowy / wiąże ścieżkę z katalogiem głównym projektu. Trzymaj regułę na poziomie sesji, nie w .claude/settings.json: /opsx:archive musi zapisywać do openspec/specs/, więc archive uruchamiaj w sesji bez niej. Składnię reguł opisuje strona Uprawnienia, sandboksy i tryby zatwierdzania.

Archiwizuj dopiero wtedy, gdy wszystkie zadania są odhaczone, a testy zielone. Skill archive ostrzega przed nieodhaczonymi zadaniami, ale po twoim potwierdzeniu i tak archiwizuje, a do tego proponuje opcję „Archive without syncing”, która przenosi zmianę do archiwum bez dotykania openspec/specs/. Przy zmianie zachowania zawsze wybieraj synchronizację.

Jak zweryfikować zmianę w OpenSpec bez czytania każdej linii?

Dział zatytułowany „Jak zweryfikować zmianę w OpenSpec bez czytania każdej linii?”

Rozdziel kontrole na deterministyczne i na te, które są oceną:

BramkaCzego dowodziCzego nie dowiedzieKto zatwierdza
Testy charakteryzacyjne przed pierwszą zmianąŻe „istniejące” wymagania są dziś prawdziweNiczego o nowym zachowaniuProgramista
Recenzja delty przed applyŻe zapisano właściwą zmianę zachowania, z przykładamiŻe kod ją realizujeWłaściciel kodu danego obszaru
openspec validate --strictStrukturę delty, tekst SHALL/MUST, scenariusz przy każdym wymaganiu, brak zgubionego scenariusza w bloku MODIFIEDŻe wymagania są słuszne albo że kod je spełniaCI
Testy napisane z każdego scenariuszaŻe kod zachowuje się zgodnie ze specyfikacją dla tych wejśćZachowania, którego nikt nie wyspecyfikowałCI
Archive aktualizuje openspec/specs/Że żywa specyfikacja odpowiada zmianieŻe kod będzie jej odpowiadał późniejRecenzent PR

Dodaj bramki strukturalne do CI. Ten job waliduje wszystkie zmiany i specyfikacje, odrzuca scenariusz zapisany z trzema krzyżykami i odrzuca pull request, który archiwizuje zmianę z deltami, nie dotykając openspec/specs/:

.github/workflows/openspec.yml
name: openspec
on: pull_request
permissions:
contents: read
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: 22
- name: Validate changes and specs
run: npx -y @fission-ai/openspec@1.13.2 validate --all --strict --no-interactive
- name: No three-hash scenarios
run: |
if grep -rnE '^### Scenario:' openspec/; then
echo "Scenarios need four hashes (#### Scenario:)"; exit 1
fi
- name: Archived deltas reached the living spec
env:
BASE_REF: ${{ github.base_ref }}
run: |
archived=$(git diff --name-only --diff-filter=A "origin/$BASE_REF"...HEAD -- 'openspec/changes/archive/*/specs/*')
if [ -n "$archived" ] && git diff --quiet "origin/$BASE_REF"...HEAD -- openspec/specs; then
echo "Archived deltas but openspec/specs/ is unchanged:"; echo "$archived"; exit 1
fi

Krok z grepem istnieje, bo walidacja nie łapie każdego źle zapisanego scenariusza. W 1.13.2 ### Scenario: pod wymaganiem, które ma też poprawny #### Scenario:, przechodzi validate --strict z samą linią INFO, że nagłówek „is ignored by validation”. Archive przebudowuje potem specyfikację, czyta zabłąkany nagłówek jako wymaganie bez scenariusza i przerywa pracę, czyli dopiero po napisaniu kodu. Grep przenosi ten błąd do pull requesta.

Taki opis to pakiet dowodowy tej zmiany. Recenzent czyta diff specyfikacji i tabelę scenariusz–test, jak opisuje recenzja pull requesta od agenta, a kod otwiera tylko tam, gdzie wiersz mówi UNTESTED albo plik leży poza listą zadań.

Oba piszą Markdown przed kodem. Różnią się jednostką pracy i tym, co przetrwa zmianę. Spec Kit zapisuje jeden katalog na funkcję, z konstytucją, specyfikacją, planem, researchem, modelem danych, kontraktami i zadaniami, i stawia bramkę na każdym etapie. OpenSpec zapisuje jeden katalog na zmianę, z propozycją, deltą, opcjonalnym designem i zadaniami, i scala deltę w specyfikację całego systemu. Tutorial Spec Kit pokazuje drugą stronę tego porównania.

Twoja sytuacjaUżyjDlaczego
Mała albo średnia zmiana w działającym systemie, kilka razy w tygodniuOpenSpecJeden punkt recenzji na zmianę, a archive utrzymuje openspec/specs/ w aktualności
Musisz odpowiedzieć z repozytorium na pytanie „co ta funkcja systemu robi teraz?”OpenSpecSpec Kit zostawia katalogi na funkcję i żadnej scalonej specyfikacji
Nowa usługa albo produkt, albo funkcja z kilkoma user stories do zatwierdzenia przez product owneraSpec KitKonstytucja, clarify, analyze i converge dają ślad z bramką na każdym etapie
Praca regulowana, która wymaga udokumentowanego planu, researchu i kontraktów dla każdej funkcjiSpec Kitdesign.md w OpenSpec jest opcjonalny i nie ma bramek etapów
Błąd, którego poprawka przywraca zachowanie opisane już w openspec/specs/Żaden frameworkNieprzechodzący test powołujący się na wymaganie
Refaktoryzacja bez zmiany zachowaniaŻaden frameworkTesty przed i po; jeśli używasz OpenSpec, ustaw skip_specs: true w .openspec.yaml zmiany

Rozsądny podział to Spec Kit dla nowej usługi i OpenSpec dla strumienia zmian po jej wdrożeniu. Wybierz jeden framework na repozytorium i zapisz to w CLAUDE.md albo AGENTS.md; dwa frameworki specyfikacji na tym samym kodzie dają agentowi dwa źródła prawdy. Porównanie frameworków spec-driven przeprowadza jedną funkcję przez oba.

Kontekst. OpenSpec instaluje skille i polecenia w projekcie, nie plugin, więc claude plugin details tu nie działa. Zmierzone 2026-09-26 na plikach, które zapisało openspec init --tools claude (1.13.2): sześć opisów skilli i sześć opisów poleceń dokłada do każdej sesji około 2200 znaków, czyli mniej więcej 550 tokenów przy czterech znakach na token. Każdy skill ładuje pełne instrukcje dopiero przy uruchomieniu: openspec-explore ma 22,8 KB, a openspec-propose 16,3 KB. Przegląd frameworków porównuje to z pakietami pluginów, które kosztują dziesiątki tysięcy tokenów.

Ceremonia. Jedna zmiana to trzy albo cztery pliki, a recenzent czyta deltę, zwykle krótszą niż strona. Prawdziwym kosztem jest dyscyplina: ktoś musi zrecenzować deltę przed apply i ktoś musi potwierdzić, że archive zaktualizował żywą specyfikację. Pomiń którykolwiek krok, a zostaniesz z katalogiem Markdownu, któremu nikt nie ufa.

Co się psuje, gdy uruchamiasz OpenSpec na istniejącym kodzie?

Dział zatytułowany „Co się psuje, gdy uruchamiasz OpenSpec na istniejącym kodzie?”

Archive odmawia przy pierwszej zmianie. Objaw: archive zatrzymuje się z komunikatem „target spec does not exist; only ADDED requirements are allowed for new specs”. Agent zapisał MODIFIED dla zachowania, które istnieje w kodzie, ale jeszcze nie w openspec/specs/, a validate --strict tego nie zatrzymał: uznaje zmianę za poprawną i wypisuje tylko linię INFO, że archive odrzuci tę deltę. Naprawa: przepisz deltę na wymagania ADDED z sekcją ## Purpose, podparte testami charakteryzacyjnymi.

Usunięcie po cichu nic nie robi. Objaw: zarchiwizowana zmiana wymienia wymaganie REMOVED, a żywa specyfikacja nigdy o nim nie wspomina. Dla obszaru bez specyfikacji archive ignoruje REMOVED z ostrzeżeniem. Naprawa: w pierwszej zmianie w ogóle nie wpisuj wycofywanego zachowania do specyfikacji, a usunięcie odnotuj w proposal.md.

Archive odrzuca deltę MODIFIED. Objaw: „current spec contains scenario(s) not present in the modified block”. Agent zapisał fragment zamiast całego wymagania. Naprawa: skopiuj pełny blok z openspec/specs/, ze wszystkimi scenariuszami, i dopiero go edytuj.

Scenariusz zostaje zignorowany, a potem archive się wywraca. Objaw: validate --strict jest zielone, testy wygenerowane z delty pomijają jeden scenariusz, a archive przerywa później z komunikatem „Requirement must have at least one scenario”. Scenariusz zapisano jako ### Scenario:, więc walidacja go zignorowała, a archive odczytał go jako wymaganie. Naprawa: zmień nagłówek na #### Scenario:, dopisz test i zostaw krok z grepem w jobie CI powyżej, żeby następny taki błąd wyszedł na recenzji.

Żywa specyfikacja rozjeżdża się z kodem. Objaw: ktoś zarchiwizował zmianę przez „Archive without syncing” albo zmienił zachowanie bez propozycji, i openspec/specs/ opisuje system sprzed kwartału. Naprawa: krok CI sprawdzający, czy zarchiwizowane delty dotarły do żywej specyfikacji, reguła CODEOWNERS na openspec/specs/ i kontrole dryfu opisane w spec-driven development.

Agent edytuje specyfikację, żeby pasowała do jego kodu. Objaw: commit implementacyjny zmienia deltę albo openspec/specs/, a testy przechodzą. Naprawa: zablokuj zapis do openspec/specs/ w sesjach implementacyjnych, wymagaj zgody właściciela obszaru na każdą zmianę w tym katalogu i chroń testy, które kodują scenariusze.

Stare tutoriale prowadzą na manowce. Objaw: npm i -g openspec instaluje wersję 0.0.0 albo /openspec:proposal nic nie robi. Naprawa: zainstaluj @fission-ai/openspec i używaj poleceń /opsx:* w pisowni twojego narzędzia.