🤖

Zettelkasten × SDD:Spec-kit × Codexでやってみた

に公開

はじめに

SDD(Specify → Plan → Implement)と spec-kit、Zettelkasten を組み合わせて、AI が迷わず実装できる土台を作るのが本題です。(Next.js でブログサイトを作ります)

用語が一般的ではないので、先にこちらの記事を参照ください:
https://zenn.dev/marux/articles/0fa715a3815010

spec-kit の参考ドキュメントはこちらです:
https://github.com/github/spec-kit

ざっくり言うと、
「SDDで仕様駆動開発した時に、Zettelkastenは保たれる?邪魔じゃない?」
の検証をしたい、という感じです。

具体的な開発フローの振り返り

1. まずは憲法(Constitution)を決める

.specify/memory/constitution.md に原則を置いて、
「Zettelkasten First」「Atomic Rules Live in Zettels」
「Specs Live in docs/specs」「TypeScript Only」
「Blog-Site Scope Discipline」などのルールを固定しました。

2. 原子仕様(ZK)を先に作る

次に ZK ノートを作成して、仕様の“核”を固定しました。

  • docs/zettels/ZK-20260101-article-definition.md
    • 記事の id, title, slug, status, publishedAt など
  • docs/zettels/ZK-20260101-article-listing-rules.md
    • 並び順、公開条件、ページサイズなど

ここが「不変の真実」なので、Spec はここを参照するだけに徹します。

3. Specify → Plan → Implement

spec-kit の流れで、機能ごとに明確に段階を切りました。

  • /prompts:speckit.specify
    • 仕様書(Spec)を docs/specs/ に作る
  • /prompts:speckit.plan
    • 実装計画・設計を固める
  • /prompts:speckit.tasks
    • 実行可能なタスクに分割して実装へ

この流れで、一覧ページ(001)も詳細ページ(002)も作成しました。
(ここは基本enterしてただけです)

How-to: 実際にやったコマンドと流れ

ここからは、より実践的に「どう進めたか」を書きます。

ステップ1: ZKと索引を作る(Codexに指示して作成)

「ブログ記事(Article)の基本データ構造と制約を定義する
ZKノートを docs/zettels/ZK-20260101-article-definition.md に作成して」

「ブログ一覧の表示ルールを定義する
ZKノートを docs/zettels/ZK-20260101-article-listing-rules.md に作成して」

「docs/indexes/features.md と docs/indexes/zettels.md を作成して
新しい ZK ノートへのリンクを追記して」

Codex は上記の指示を受けて、ZK ノートと索引を作成し、憲法の「Atomic Rules Live in Zettels」を満たす形に整えてくれました。

ステップ2: Spec を作る

/prompts:speckit.specify "Create a blog post listing page..."
/prompts:speckit.specify "Create an article detail page..."

ここで AI は ZK を読み、
「Spec に ZK の内容を重複させない」という制約を守りながら、
必要な要件の穴を補ってくれました。

ステップ3: Plan → Tasks

/prompts:speckit.plan docs/specs/001-blog-listing-page/spec.md
/prompts:speckit.tasks

/prompts:speckit.plan docs/specs/002-article-detail-page/spec.md
/prompts:speckit.tasks

Plan では技術スタックや設計方針を固め、Tasks で実装手順に落とし込み。
AI はここまでを通じて「どう作るべきか」を自律的に整えます。

ステップ4: 実装

# 実装開始
/prompts:speckit.implement

実際の実装は tasks.md の順番に沿って進行。
AI がタスクをチェックしながら進めるので、
「どこまでやったか」「次に何をやるか」が常に明確でした。

ステップ5: 確認

# 実装開始
npm install
npm run dev


綺麗なサイトですね ^ ^

実際に起きたトラブルと解決

1. 日付のズレ

一覧ページで「日付表示がブラウザ依存」でズレる問題が発生しました。
具体的には toLocaleDateString() の出力差で SSR と CSR が不一致になり、
Hydration Error が出ました。

解決: date-fnsformat を使い、
yyyy/MM/dd 固定フォーマットで表示することでズレを解消。

この修正は、Plan に「表示形式は固定」と追記する形で「設計に還元」しました。

2. specs ディレクトリのパス問題

spec-kit のスクリプトは specs/ を前提にしていましたが、
憲法上は docs/specs/ が正しい。
このズレが原因で Plan/Tasks の出力先が一致しない問題が発生。

解決: specsdocs/specs へシンボリックリンクし、
スクリプトの前提を壊さずに憲法を守る形に調整しました。

ここも「場当たり的修正」ではなく、
Constitution のルールを守るための“設計的解決”になっています。

図解で見るフロー

全体の流れ

基本的なSDDの流れですが、改めていいですね。
specの参照もクリーンな感じがします。

Zettelkasten 的なドキュメント管理の検証

docs/ 構造が「知識の最小単位」を支える

実際のリポジトリ構成を含めると、こんな形です。

zettelkasten-sdd/
├── content/
│   └── articles/
│       └── test-article.md
├── docs/
│   ├── indexes/
│   │   ├── features.md
│   │   └── zettels.md
│   ├── specs/
│   │   ├── 001-blog-listing-page/
│   │   │   ├── data-model.md
│   │   │   ├── plan.md
│   │   │   ├── quickstart.md
│   │   │   ├── research.md
│   │   │   ├── spec.md
│   │   │   └── tasks.md
│   │   └── 002-article-detail-page/
│   │       ├── data-model.md
│   │       ├── plan.md
│   │       ├── quickstart.md
│   │       ├── research.md
│   │       ├── spec.md
│   │       └── tasks.md
│   ├── zettels/
│   │   ├── ZK-20260101-article-definition.md
│   │   └── ZK-20260101-article-listing-rules.md
│   └── llm/
├── src/
│   ├── components/
│   ├── lib/
│   ├── pages/
│   └── services/
└── .specify/

この構成で役割がはっきり分かれます。

  • docs/zettels/ = 原子仕様の置き場(不変の真実)
  • docs/specs/ = 機能仕様の置き場(設計の一時領域)
  • docs/indexes/ = 索引(features / zettels の導線)
  • docs/llm/ = AI 用のガイドや補助情報

ここで重要なのは、実際にファイル同士がリンクで繋がっていることです。

たとえば docs/indexes/zettels.md には次のようなリンクがあります。

  • ZK-20260101-article-definition../zettels/ZK-20260101-article-definition.md
  • ZK-20260101-article-listing-rules../zettels/ZK-20260101-article-listing-rules.md

さらに docs/indexes/features.md も ZK へのリンクを持っています。

  • ZK-20260101-article-definition../zettels/ZK-20260101-article-definition.md

そして Spec 側では、docs/specs/001-blog-listing-page/spec.md
docs/specs/002-article-detail-page/spec.md の両方が
docs/zettels/ の該当ノートを参照しています。

実際の Spec から抜粋すると、こんな記述があります。

FR-002: System MUST follow the listing behavior defined in
docs/zettels/ZK-20260101-article-listing-rules.md without duplicating those
rules in this spec.

FR-002: System MUST load the markdown file associated with the requested
slug and parse it into article data per
docs/zettels/ZK-20260101-article-definition.md.

ここで重要なのは、Spec 同士が直接リンクしていないことです。
それでも整合性が保たれているのは、ZK をハブにする構造が
「ドキュメントの正規化」を実現しているからです。

1. 「スパゲッティ・スペック」から「ハブ・アンド・スポーク」へ

通常のドキュメントでは「一覧仕様は詳細仕様の〇〇に従う」など、
機能間依存が増えがちで、これがスパゲッティ化を招きます。

このリポジトリでは、以下の構図が成立しています。

  • Hub(中心): ZK-20260101-article-definition.md
  • Spoke(スポーク): specs/001-blog-listing-page/spec.md
    specs/002-article-detail-page/spec.md

一覧(001)と詳細(002)は互いの存在を知る必要がなく、「記事とは何か?」という中央の真実(ZK)だけを見ていれば、整合性が自動的に保たれます。

2. AI にとっての「計算効率」と「S/N 比」の最大化

詳細ページ(002)を実装するとき、AI が一覧ページ(001)の UI やページネーションを読む必要はありません。
AI は「002 の Spec」と「参照先の Zettels」だけ読めば十分で、ノイズが減り、S/N 比が最大化されます。

3. 索引(Indexes)こそが自律性を支える

直接リンクがない代わりに、docs/indexes/zettels.md が地図になります。
AI はまず索引をスキャンし、必要な ZK へ自律的に辿り着きます。

実際の docs/indexes/zettels.md はこのように明示的なリンクを持っています。

この「地図 → 原子仕様 → Spec」という動線が、人間の細かい指示を減らし、AI の探索性を高めます。

4. 知識の正規化がもたらす未来

ZK が増えるほど、AI は「過去の教訓」を参照して新しい Spec を検閲できます。
たとえば Spec-003 を作るときも、既存 ZK と矛盾しないかを自動検知できる。

結論

機能同士が直接繋がっていないことは、情報の依存関係を排除し、知識を純粋な状態で管理できている証拠です。通常の SDD が「機能の積み上げ」だとすれば、Zettelkasten 導入型 SDD は「不変の知識のネットワーク化」です。だからこそ、機能が増えてもドキュメントは複雑化せず、むしろ AI にとっての「理解の軽さ」が加速していきます。

ZK が「これ以上分解できない知識」として機能し、Spec はそれを引用するだけにすることで、仕様の“重複コピー”を防げました。

おわりに

SDDを動かしつつ、仕様の整理はZKの規律に従うように動作させました。これといった違和感なく、機能開発をSDDツールを利用して行うことができました!
ZKの規律が、SDDツールの邪魔をしないか?という観点では問題なさそうです。
この方針を成立させる基盤(仕様インポートなど)を整えるのが一番大変そうですが、なかなか良さそうです。

Discussion