コンテンツにスキップ

Semantic (Jev)

HEAL のメトリクスは、ソースと git の履歴からローカルで計算します。コードのどこが変えにくいかは分かりますが、コードが何を意味しているかまでは分かりません。たとえば、関数の名前が中身と合っているか、テストが本当に何かを確かめているか、ドキュメントの説明がまだコードと合っているか、といったことです。

オプトインの [features.semantic] は、こうした問いを TypeSafe の分類モデル Jev に投げます。Jev は文章やコードを書きません。問いごとに確率で答えるので、heal はほかのメトリクスと同じように、その答えを Finding にできます。

このファミリの機能は、どれも既定では無効です。有効にしなければ、heal の動きは今までと変わりません。

  • 送信するのは heal semantic ask だけです。heal があらかじめ選んだコード・テスト・ドキュメントの一部(よく変更されるファイルの関数など)を、TypeSafe の API に送ります。
  • heal auth jev status は、API キーが使えるかを確かめます。キーは送りますが、プロジェクトの中身は送りません。
  • それ以外のコマンド(heal status、heal metrics、heal diff、post-commit フック)は、ネットワークにつながりません。heal semantic ask が保存した答えを読むだけです。
  • [features.semantic].exclude に一致するファイルは送りません。

TypeSafe は、利用者のデータをモデルの学習に使わないとしています。データを保持しない契約(Zero Data Retention)は enterprise プランだけです。非公開のコードベースで有効にする前に、TypeSafe の規約 を確認してください。

有効にするかどうかはチームで決めることなので、共有の設定ファイルに書きます。

[features.semantic]
enabled = true
# model = "jev-1.13.0" # バージョンを固定する。動くエイリアスは使えない
# max_usd = 1.0 # 1 回の実行で、これ以上使う前に止める
# concurrency = 8 # 同時に送るリクエストの数
# exclude = ["secrets/", "*.pem"]

有効にすると、heal status は保存された答えも使って TODO リストを並べます。/heal:refactor・/heal:docs・/heal:tests の各スキルは、自分の提案とコミットを Jev に確かめさせます。

キーは一人ひとりが自分のものを使います。TypeSafe のコンソール で取得したら、環境変数に入れるか、

export TYPESAFE_API_KEY=...

ユーザー設定(プロジェクトの外にあり、本人しか読めないファイル)に保存します。

printf '%s\n' "$KEY" | heal auth jev set
heal auth jev status # キーの出どころを表示し、使えるかを確かめる
heal auth jev clear # 保存したキーを消す

キーが .heal/ の下に書かれることはありません。両方あるときは TYPESAFE_API_KEY が優先されます。TYPESAFEAI_API_KEY も読むので、ほかの Jev のツールで使っているキーをそのまま使えます。

heal semantic ask --dry-run # 何を問い合わせるかと、料金の見積もり
heal semantic ask # 問い合わせて、答えを保存する
heal semantic ask --prune # あわせて、どこからも参照されなくなった答えを消す

答えは .heal/semantic/verdicts/ に、タスクごとに 1 ファイルで保存されます。このディレクトリはコミットしてください。API キーを持たないチームメンバーも同じ結果を見られるようになり、heal も前回から変わったコードについてだけ問い合わせます。2 つのブランチの両方で答えが増えたときは、次の設定でマージの衝突を減らせます。

.gitattributes
.heal/semantic/verdicts/*.jsonl merge=union

下で説明する、その場で行う確認と focus の答えは、一人の作業途中の状態についての答えなので、代わりに .heal/cache/semantic/verdicts/ に保存します。このディレクトリは git の管理から外れていて、消しても問題ありません。

問いの種類ひとつひとつをタスクと呼びます。[features.semantic.tasks.<id>] enabled = false でタスクを止められ、cutoff = 0.7 のように書けば判定に必要な確率を変えられます。

タスク 何を問うか どこに表れるか
commit_intent 最近の各コミットが、バグ修正、機能追加、リファクタなどのどれか バグ修正が集中しているファイルが、heal status のリストで上に来る
consequence フラグの立った各ファイルに欠陥があったとき、開発ツールの不調からデータの破損まで、どれほどの損害になるか 重要なコードが、heal status のリストで上に来る
triage 解消キューの Finding が、機械的に直せるものか、誤検知か、設計の判断が要るものか。修正はどれくらいの大きさか 作業するスキルの判断材料になり、小さな修正が先に来る
friction 複雑なコードが、変えにくいのか、テストしにくいのか、読みにくいのか。その複雑さは本質的なものか 本当に困っているコードが上に来る
focus 説明した作業が、各ファイルにどれくらい触れるか(--focus) heal status --focus で、先に整えておくべきファイルが上に来る
concept 各関数が、語彙のうちどの概念を実装しているか(インラインの単体テストは除く) 概念が混ざったファイル、置き場所の違う関数、多くのファイルに散らばった概念
term_drift 関数名に使われた 2 つの語が、同じものを指しているか(user と account など) 1 つのものには 1 つの語を使うための、名前の変更の提案
name_mismatch 関数の名前やドキュメントコメントが、本体の処理と合っているか コードがしていないことを約束している名前
split_points 長く複雑な関数を、目的が 1 つずつの手順に分けるならどこで区切るか 複雑度の Finding に付く区切り位置。/heal:refactor が使う
fix_pattern 複雑度や重複の Finding に、どの定番のリファクタが合うか /heal:refactor の判断材料
test_value 各テストが、名前にうたった振る舞いが壊れたときに気づけるか。それとも自分のモックやフレームワークしか確かめていないか 消すか書き直すべきテスト([features.test] が必要)
mock_scope 各モックが何を置き換えているか。外部サービスか、テスト対象のコードか、内部の部品か テストを実装の細部に縛りつけているモック
test_triage テストのないコードが、単純なロジックか、処理の取りまとめか、I/O か。skip されたテストはなぜ skip されているか 単体テストが効く場所を /heal:tests に伝える
test_duplicate 似た 2 つのテストが同じことを確かめているか。入力が違うだけか 消すべきテストや、1 つのテーブル駆動テストにまとめるべきテスト
doc_structure 各セクションがどんな種類の文書か(チュートリアル、ハウツー、リファレンス、解説など)。新しい文書はどこから始まるか 分けるべきページ、まとめるべきページ、1 つの種類に絞るべきページ([features.docs] が必要)
doc_placement 読む人は、各ページをドキュメントのどのセクションで探すか 置き場所の違うページと、孤立ページをどこからリンクするか
doc_drift_semantic ペアになったドキュメントのセクションが、まだコードの動きを説明しているか コードがもうしていないことを書いているセクション
doc_concept 各ドキュメントのセクションが、語彙のうちどの概念を説明しているか コードが頼っているのに、どのドキュメントも説明していない概念
doc_overlap 2 つのページが同じ概念を 2 回説明していないか、食い違っていないか まとめるべき説明と、直すべき食い違い

答えを保存していても、heal status が Finding を Tier と Severity でまとめる点は変わりません。そのうえで各グループの中では、次のものを先に並べます。欠陥の損害が大きいファイル、扱いにくいコード、バグ修正が続いているファイル、小さく済む修正です。それでも決まらなければ、いつもの Hotspot のスコアで決めます。答えが保存されていなければ、並び順は変わりません。スキルも、自作のスクリプトも、同じ並び順を heal status --json から読めます。キューに入っている各 Finding には、drain_rank(1 が次に取り組むもの。ファミリごとに数える)と drain_tier が付いています。

決まった作業に備えたいときは、その作業をファイルに書いてから次を実行します。

heal semantic ask --task focus --focus plan.md
heal status --focus plan.md

concept タスクには、コードを形づくっている考え方の一覧が必要です。.heal/concepts.toml に書きます。Jev は与えられた名前の中から選ぶだけで、新しい名前を作ることはありません。最初の案はセットアップ用のスキルで作り、見直してからコミットしてください。Claude Code で次を実行します。

/heal:setup semantic
[[concept]]
id = "calibration"
description = "Derives thresholds from the project's own metric distribution."

/heal:refactor、/heal:docs、/heal:tests の各スキルは、その場で行う確認(verify_patch、verify_tests、verify_proposal、name_choice)も --task で実行します。verify_patch と verify_tests は、--diff で渡したコミット(たとえば --diff HEAD~1..HEAD)を判定します。これらはエージェントが自分の仕事を確かめ直すためのもので、引数なしの heal semantic ask で実行されることはありません。

Jev の料金は入力だけにかかります(執筆時点で、入力 10 億トークンあたり 42 ドル)。--dry-run を付けると、送る前に見積もりを表示します。max_usd を設定しておけば、上限を超える前に実行を止めます。中くらいの規模のリポジトリなら、1 回の実行はたいてい数セントです。

分類モデルは間違えることがあります。しきい値に近いスコアは、モデルのバージョンによって少し変わることもあります。HEAL はモデルのバージョンを固定し、保存した答えを使い回して結果を安定させていますが、semantic の Finding は、人が確かめるための候補として扱ってください。そのまま従うべき指示ではありません。

enabled = false にします。次の heal status で semantic の Finding はすべて消え、通常の並び順に戻ります。保存した答えは、自分で消すまで .heal/semantic/ に残ります。