コンテンツにスキップ

AI が速くコードを書く時代に、仕様書との乖離を止めるために jev-spec という OSS を作っている話

jev-spec: Catch spec drift on every commit.

AI がコードを速く書く現場で起きていること

Section titled “AI がコードを速く書く現場で起きていること”

半年ほど前に、Markdown ドキュメント間の整合性を静的解析する contextlint というリンターの 記事 を書きました。

仕様駆動開発(SDD)の現場では、ビジネス上の「要件」をシステムの振る舞いである「仕様(受け入れ基準)」へ落とし込み、さらにテーブル定義や画面構成などの「設計」へと具体化しながら、それらを Markdown に書き起こして Cursor や Claude Code などの AI コーディングエージェントへの入力として渡す開発スタイルが定着しつつあります。

AI は驚くべき速度でコードを書き進めてくれます。しかしコードの生成速度が上がるほど、開発者が元の仕様書を読み返す機会は相対的に減っていきます。日々のレビューでは目先のコード差分や動作確認に意識が向きがちで、仕様書の一文一文と実装されたコードの振る舞いが一致しているかまでを常に確認し続けるのは困難です。その結果、仕様書とコードのあいだに誰も気づかないまま乖離が蓄積していきます。

仕様書の中に REQ-AUTH-02(※ここでは一例として振っている要件・仕様の識別子です)のような記述が存在し、書式が正しく、ドキュメント間で正しく参照されている。ここまでは静的解析(contextlint など)で確認できます。しかし「src/auth/session.ts は今も REQ-AUTH-02 のとおりに動くのか」は、文書の構文解析だけでは分かりません。

この問題に対処するため、現場では Cursor のスラッシュコマンドや Claude Code の Agent Skill を作成し、定期的にコードベースを巡回して仕様書との乖離を検知しようとする工夫も見られます。「このコードが仕様書の要件を満たしているか照合してほしい」とエージェントに依頼するアプローチです。

しかし、このアプローチを毎回のコミットで回すことには無理があります。巨大なコードと仕様書をプロンプトに詰め込んで汎用の LLM エージェントを走らせると、1 回の実行に数分単位の時間がかかり、トークン消費による費用もかさみます。あまりに遅く、あまりに高価であるため、毎回の git commit を遮る pre-commit フックに組み込むことはできません。結果として乖離の検知は「気が向いたときに走らせる重い調査」にとどまり、日々の開発の裏側で乖離が進行していくことになります。

「毎回のコミットで、コンマ数秒のうちに仕様書とコードの乖離を止められないか」。この課題意識から開発を始めたのが、今回紹介する OSS jev-spec です。

jev-spec
Catch spec drift on every commit: check your code against your Markdown specs with TypeSafe AI's Jev model.
🔗github.com

従来の LLM の限界と、Jev というアプローチ

Section titled “従来の LLM の限界と、Jev というアプローチ”

コミットのたびに仕様書とコードを突き合わせるゲートを作ろうとしたとき、従来の LLM には構造的な壁が存在していました。

汎用の LLM は、すでに出力したトークンを条件に次のトークンを選択する自己回帰的なデコーディングが基本です。JSON モードや Structured Outputs でスキーマを指定しても、出力はスキーマに適合するトークン列として順にサンプリングされる点に変わりはなく、スキーマは次に取り得る正当なトークンを制約するだけで生成方式そのものを変えるわけではありません。出力が長くなるほど待ち時間と出力トークンの費用はかさみますが、待ち時間には入力の処理や推論トークンの生成、初回のスキーマコンパイルも加わります。課金も出力トークンだけでなく、入力トークンに対して別途発生します。ローカルの pre-commit で開発者の作業リズムを崩さないためには、検証は 1 秒未満で終わる必要があります。待ち時間が長く、実行のたびに課金が発生する仕組みを、毎回のコミットに挟むことはできません。

この制約に対して新しい切り口を提示したのが、TypeSafe AI が開発した Jev というモデルです。TypeSafe はこれを「System One モデル」と呼んでいます。

System One - TypeSafe
System One models make fast, structured decisions for software. Jev is TypeSafe's flagship model and the first System One model.
🔗docs.typesafe.ai

Jev の仕組みを正確に説明すると、自由形式の文章を自己回帰的に逐次生成するデコーディングを行いません。その代わりに、与えられたコンテキストや状態(テキスト、データ、アプリケーションの状態など)を一度の評価で直接推論し、型付けされた決定プリミティブ(確からしさを表す確率やスコア)を出力します。jev-spec はこの能力を活かし、仕様書とコードをコンテキストとして Jev に渡し、仕様項目ごとの Rubric に対する確率を得ます。

文章の逐次生成を行わないため、出力トークンの待ち時間がなく、出力トークンに対する課金もありません。入力トークン費用も 100 万トークンあたり $0.042 と極めて低価格に抑えられており、応答時間はわずか 70〜400ms 程度で完了します。

Jev には 3 つの決定プリミティブが備わっています。

  • noul(真偽判定)

    Yes か No で答えられる命題に対し、真である確率を 0 から 1 の数値で返す

  • choice(カテゴリ分類)

    宣言された排他的な選択肢(たとえば compliant、vulnerable、inconclusive など)に対する離散確率分布を返す

  • score(順序評価)

    未実装から本番水準までといった段階的な評価尺度に対して、期待スコアと各段階の確率を返す

Jev の答えは最初から信頼できる数値として得られるため、自然言語のパース処理を挟む必要がありません。得られた確率をしきい値(たとえば minProbability: 0.85)と比較するだけで、確定的な合否判定を下せます。

広がる可能性と、jev-spec の着想

Section titled “広がる可能性と、jev-spec の着想”

ここで触れておきたいのは、Jev は仕様書の検証のためだけに作られたモデルではないという点です。

X(旧 Twitter) のコミュニティを見渡すと、開発者たちはこの超高速かつ安価な決定プリミティブを活用し、これまでのソフトウェア開発では難しかった新しい体験を試行錯誤しています。

象徴的な例として、Marcus Lowe 氏の スマートクリップボードのデモ では、クリップボードにコピーされた氏名や連絡先といったテキストを瞬時に判別し、対応する入力フィールドへミリ秒単位でルーティングする UI/UX が示されています。OkinaAudio 氏の Ableton 向けクリエイティブ・オーディオ・ワークフロー では、高速なオーディオと UI の相互作用が実演されています。

自由形式のテキスト生成とは異なり、ミリ秒単位で型付けされた決定を下すという System One モデルならではの特性は、新しいソフトウェアの相互作用を可能にします。エンジニアとして、この技術の流れには素直な興奮を覚えます。

そして、この Jev の特性に触れたとき、Markdown ドキュメントの静的解析ツール contextlint を作ってきた私の頭にひとつの発想が浮かびました。

「この高速な決定プリミティブがあれば、毎回の git commit でコードが仕様書に書かれた振る舞いや要件を満たしているか判定できるのではないか」

この高速な決定モデルを、仕様駆動開発の現場でも活用できないかと考えたのが jev-spec を作り始めたきっかけです。

jev-spec が行っている処理の流れは次の 4 つです。

  1. 仕様書を読む

    Markdown の仕様書から、セクションや見出し、あるいは仕様・要件 ID などに基づいて検証したい記述を抽出する

  2. コードを読む

    その仕様を実装しているソースコードを読み込む

  3. Rubric を評価する

    仕様項目ごとに焦点を絞った問い(Rubric)を Jev に送る。Jev は仕様書とコードをコンテキストとして一度の評価で直接推論し、すべての Rubric に対して並列に確率を返す

  4. 比べる

    返ってきた確率をしきい値(Assertion)と比較し、1 つでも違反があれば検証エラー(チェック失敗)として判定する

判定の問いを Rubric、しきい値との比較を Assertion と呼びます。合否判定では真偽判定を行う noul が中心になりますが、DSL では選択肢を判定する choice や段階評価の score も利用できます。

言語処理系には、新しい Go 製コンパイラによって高速なコンパイルと厳密な型安全性を実現した TypeScript 7 を採用しています。さらに実行ランタイムとして、Node.js(22 以上)と Bun の双方を公式にサポートするデュアルランタイム構成をとっています。

実際のプロジェクトで jev-spec を導入する際の流れはシンプルです。

Agent Skills 対応のクライアント(Claude Code、Cursor、Codex、Antigravity CLI、GitHub Copilot)では、次のコマンドで導入できます。

ターミナルウィンドウ
gh skill install nozomi-koborinai/jev-spec <スキル名>
スキル役割
jev-spec-init仕様とコードの対応付けから Rubric の作成までを行う
jev-spec-fix失敗したチェックの修正を支援する

パッケージマネージャーを使って開発依存(devDependencies)に追加します。

ターミナルウィンドウ
npm install -D jev-spec
# または
bun add -d jev-spec

2. 設定ファイルの作成(jev.config.ts)

Section titled “2. 設定ファイルの作成(jev.config.ts)”

プロジェクトルートに jev.config.ts を作成し、仕様書とコードの対応関係を targets の中に記述します。

import { defineConfig, noul } from 'jev-spec';
export default defineConfig({
client: { model: 'jev-1.13.0' },
targets: {
auth: {
specPath: 'docs/specs/auth-requirements.md',
codePaths: ['src/auth/**/*.ts', '!src/auth/**/*.test.ts'],
rubrics: {
'REQ-AUTH-01': noul(
'Is the signature of a session token checked before access to a protected resource is granted?'
),
'REQ-AUTH-02': noul('Is a token rejected when its ID is on the revocation list?'),
},
assertions: {
'REQ-AUTH-01': { minProbability: 0.85 },
'REQ-AUTH-02': { minProbability: 0.85 },
},
},
},
});

defineConfig で設定する主な項目は次の通りです。

  • specPath

    対象となる Markdown 仕様書のファイルパス

  • codePaths

    仕様を実装しているコードの glob パターン(テストファイルを除外するなどの指定が可能)

  • rubrics

    noul(...) を使って、コードが満たすべき仕様・振る舞いを自然言語の問いとして記述(キーには REQ-AUTH-01 のような ID や、対象の仕様項目名を指定)

    ※ ID による指定はあくまで一例です。プロジェクトで要件 ID などを厳密に振っていなくても、任意のキー名で管理できます。

  • assertions

    合格とみなす最小確率のしきい値(例: minProbability: 0.85)

Jev の推論を実行するために、TypeSafe AI の API キーを環境変数に設定します。

ターミナルウィンドウ
export TYPESAFE_AI_API_KEY="your-api-key"

設定が完了したら、CLI からチェックを実行します。

すべてのターゲットを網羅的に検証する場合は、引数なしで実行します。

ターミナルウィンドウ
npx jev-spec check
# または
bunx jev-spec check

Git でステージングされている変更だけを対象にして高速に検証する場合は、--staged フラグを付けます。差分に関連するターゲットだけが自動で選択され、それ以外は SKIPPED となります。

ターミナルウィンドウ
npx jev-spec check --staged

実行結果はターミナルに見やすく整形されて出力されます。

$ npx jev-spec check
=== jev-spec Check Report ===
Target: auth [✖ FAILED]
Spec files: docs/specs/auth-requirements.md
Code files: src/auth/session.ts
Model: jev-1.13.0
✔ REQ-AUTH-01: probability: 0.97
✖ REQ-AUTH-02: probability: 0.08
└─ Violation: Probability 0.08 is below minimum threshold 0.85
Overall: ✖ CHECKS FAILED
$ echo $?
1

仕様項目(ID)ごとに確率としきい値の比較結果が表示されるため、どの仕様とコードが乖離しているのかを一目で特定できます。

jev-spec は、開発フローの日常的なゲートとして組み込むことで最も活きてきます。

  • pre-commit フックでの差分チェック

    husky や simple-git-hooks などを使い、コミット時に npx jev-spec check --staged を実行します。ミリ秒単位で変更対象のターゲットのみを判定するため、開発者の作業リズムを崩さずに仕様とのずれをコミット前に防げます

  • CI での全体検証

    GitHub Actions などの CI パイプラインで、リポジトリ全体を対象に npx jev-spec check を実行します。プルリクエストの時点で仕様書とコードの整合性を確実に担保します

確率で答えるモデルをビルドゲートに置くにあたり、コストと再現性について、自分自身の仕様書(docs/specs/)を持つ jev-spec のリポジトリで実測を行いました。以下の数値はすべて 2026 年 9 月 21 日に jev-1.13.0 に対して計測した結果です。

リポジトリ全体を対象に 8 つのターゲット、合計 22 個の Rubric すべてをチェックした場合でも、所要時間は約 4 秒で完了しました。1 ターゲットあたりにかかる時間は 0.3〜0.9 秒程度です。CLI の起動そのものは Node.js で約 0.1 秒、Bun ではさらに高速で、所要時間の大半は API との通信時間でした。

レポートに出力された見積もりコストは、1 回の実行あたり約 $0.0006 でした。リポジトリ全体をプロンプトに詰め込むのではなく、ターゲットごとに必要な仕様書の抜粋と数ファイルのコードだけを送り、Jev が一度の評価ですべての Rubric にまとめて答えるため、コミットごとに実行しても気になる金額にはなりません。

同じコードに対して同じ Rubric を 5 回繰り返したとき、正しいコードに対する確信度のばらつきは 0.00〜0.03 の範囲内に収まりました。0.96 が 0.95 になる程度の揺らぎであり、設定ファイルで指定した 0.85 という例示のしきい値(minProbability)を割り込むことはありませんでした。

しきい値は固定値ではなく、仕様や要件の性質に応じて設定ファイルで個別に調整できます。また、latest などのエイリアスが指すモデルが更新されると判定結果が意図せず変動する恐れがあるため、設定の client.model にはバージョン付きの ID(jev-1.13.0 など)を指定して固定することを推奨しています。モデルを明示的に固定することで、意図しない判定のずれを防ぎ、レポートにも実際に判定したモデル名が明記されます。

静的解析と決定モデルの役割分担・おわりに

Section titled “静的解析と決定モデルの役割分担・おわりに”

jev-spec を実践で運用するにあたっては、その限界も把握しておく必要があります。

  • 証明ではなくチェックであること

    合格は確率がしきい値を超えたことを意味し、数学的な正しさの証明ではありません。そのためツール内の用語も verify ではなく check に統一しています

  • 英語での記述が推奨されていること

    Jev の公式ドキュメントでは英語での記述が推奨されており、日本語を含む CJK 言語は英語と同等の精度ではないとされています。ただし実用上は、日本語で書かれた仕様書や Rubric でも十分に意図を理解し、実用的な精度で判定を行えます

  • コード内の自己主張コメントに影響されうること

    「この関数は仕様を満たしている」といった自己主張的なコメントがあると、モデルの判断が引っ張られる可能性があります

  • 厳密な計算や日時の比較は不得手であること

    数値のカウントや日時の比較、多段の論理推論を要する確認は Rubric から外し、通常のユニットテストで担保します

静的解析ツール contextlint と、意味検証エンジン jev-spec の役割分担を整理すると、次のようになります。

contextlintjev-spec
検証対象文書どうしの整合性仕様書とコードの一致
アプローチルールベースの静的解析モデルによる Rubric の判定としきい値比較
結果決定的確率的
AI への依存なしあり(TypeSafe AI の Jev)

AI がコードを高速に書き出す時代だからこそ、コードと仕様書の乖離を放置せず、開発のリズムを損なわない速度で守り続ける仕組みが求められています。

jev-spec は私の思いつきで始めたものですが、仕様書を Markdown で管理し、コードとの乖離をどう防ぐか悩んでいる方は、ぜひ試してみてください。

jev-spec - npm
Catch spec drift on every commit: check your code against your Markdown specs with TypeSafe AI's Jev model.
🔗npmjs.com